以上是标准库 ioutil.ReadAll 的代码,每次会创建一个 var buf bytes.Buffer 并且初始化 buf.Grow(int(capacity)) 的大小为 bytes.MinRead, 这个值呢就是 512,按这个 buffer 的大小读取一次数据需要分配 2~16 次内存,天啊简直不能忍,我自己创建一个 buffer 好不好。
看一下火焰图🔥吧,其中红框标记的就是 ioutil.ReadAll 的部分,颜色比较鲜艳。

优化读取方法
自己创建足够大的 buffer 减少因为容量不够导致的多次扩容问题。
- buffer := bytes.NewBuffer(make([]byte, 4096))
- _, err := io.Copy(buffer, request.Body)
- if err !=nil{
- return nil, err
- }
恩恩这样应该差不多了,为啥是初始化 4096 的大小,这是个均值,即使比 4096 大基本也就多分配一次内存即可,而且大多数数据都是比 4096 小的。
但是这样真的就算好了吗,当然不能这样,这个 buffer 个每请求都要创建一次,是不是应该考虑一下复用呢,使用 sync.Pool 建立一个缓冲池效果就更好了。
以下是优化读取请求的简化代码:
- package adapter
-
- import (
- "bytes"
- "io"
- "net/http"
- "sync"
-
- "github.com/json-iterator/go"
- "github.com/sirupsen/logrus"
- "github.com/thinkeridea/go-extend/exbytes"
- )
-
- type Adapter struct {
- pool sync.Pool
- }
-
- func New() *Adapter {
- return &Adapter{
- pool: sync.Pool{
- New: func() interface{} {
- return bytes.NewBuffer(make([]byte, 4096))
- },
- },
- }
- }
-
- func (api *Adapter) GetRequest(r *http.Request) (*Request, error) {
- buffer := api.pool.Get().(*bytes.Buffer)
- buffer.Reset()
- defer func() {
- if buffer != nil {
- api.pool.Put(buffer)
- buffer = nil
- }
- }()
-
- _, err := io.Copy(buffer, r.Body)
- if err != nil {
- return nil, err
- }
-
- request := &Request{}
- if err = jsoniter.Unmarshal(buffer.Bytes(), request); err != nil {
- logrus.WithFields(logrus.Fields{
- "json": exbytes.ToString(buffer.Bytes()),
- }).Errorf("jsoniter.UnmarshalJSON fail. error:%v", err)
- return nil, err
- }
- api.pool.Put(buffer)
- buffer = nil
-
- // ....
-
- return request, nil
- }
(编辑:应用网_丽江站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|