-4

I have an application which is reading a large number of requests (approx 4-5 million reqs / second)

I am using json.decoder to read my incoming json data from the request body.

my code :

err := json.NewDecoder(c.Request.Body).Decode(&obj)

The above code snippet drains the request body. Is there any way I could stream it back so it can be read again in the code?

For instance, while using the io util library, I could just do :

c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

The solution I'm looking at is something like :

err := json.NewDecoder(c.Request.Body).Decode(&obj)
if err != nil {
    return err
}
b := new(bytes.Buffer)
c.Request.Body = json.NewEncoder(b).Encode(obj)

but obviously c.Request.Body = json.NewEncoder(b).Encode(obj) is not supported

I can't use the ioutil library since it gets very difficult to manage lengthy requests.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
mythic
  • 535
  • 7
  • 21
  • The only way to read a reader without draining it is to buffer its contents (either in memory or on disk typically). – Jonathan Hall Aug 09 '21 at 09:05
  • 2
    You haven't really explained your problem very well, though. The three "solutions" you've said you've tried have no apparent commonality. It's not clear how any of them, let alone all of them, might solve your problem. Can you explain your problem more clearly? – Jonathan Hall Aug 09 '21 at 09:06
  • Umarshaling and marshaling again will always be slower than reading into a buffer and set a Reader that produces the buffer's content. So just do that. See [Golang read request body multiple times](https://stackoverflow.com/questions/43021058/golang-read-request-body-multiple-times/43021236#43021236) – icza Aug 09 '21 at 10:33
  • 1
    `however I see the response time increasing over time` this rings a bell in my mind about the GC. And i would like to suggest to reduce your allocations using a `sync.Pool` and `io.CopyBuffer`. But i also agree to Flimzy's comment, your post lacks clarity. –  Aug 09 '21 at 10:54
  • @Flimzy Please check now, I have simplified the problem statement – mythic Aug 09 '21 at 12:07

2 Answers2

0

If your only goal is to re-populate the request body, you've already shown how to do that:

c.Request.Body = ioutil.NopCloser(bytes.NewReader(bodyBytes))

If you want to do this from the result of a json encoder, it's just as easy:

bodyBytes, err := json.Marshal(obj)
if err != nil { /* error handling */ }
c.Request.Body = ioutil.NopCloser(bytes.NewReader(bodyBytes))

However, as mentioned in comments, this will be MANY TIMES less efficient than buffering the request body, as you seem to have already been doing.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
0

The example of io.TeeReader

package main

import (
    "io"
    "os"
    "strings"
)

func main() {
    var r io.Reader = strings.NewReader("some io.Reader stream to be read\n")

    r = io.TeeReader(r, os.Stdout)

    // Everything read from r will be copied to stdout.
    io.ReadAll(r)

}
wu qingtao
  • 11
  • 2