From 52be57c84d0077f95e76e0358b419985b91e9572 Mon Sep 17 00:00:00 2001 From: Kyle Xiao Date: Tue, 3 Mar 2026 15:12:40 +0800 Subject: [PATCH] refactor(network): optimized client mem usage Use Read() instead of Peek()+Skip() in ReadByte() and appendBodyFixedSize() to avoid holding references to the underlying buffer. Peek() returns slices that reference the internal buffer, preventing it from being released or reused until the reference is dropped. Also updated cloudwego/gopkg dependency and moved connstate import to gopkg/net/connstate. --- .codecov.yml | 3 +++ go.mod | 4 ++-- go.sum | 8 ++++---- pkg/app/server/hertz_test.go | 5 ++--- pkg/network/standard/connection.go | 16 ++++++++-------- pkg/network/standard/transport.go | 2 +- pkg/network/standard/unix_test.go | 2 +- pkg/protocol/http1/ext/common.go | 6 ++++++ 8 files changed, 27 insertions(+), 19 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index 3881dfe42..98a4470fb 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -13,3 +13,6 @@ coverage: # pass if coverage drops by no more than 0.05% # this is possibly caused by unstable coverage. threshold: 0.05% + patch: + default: + target: 80% diff --git a/go.mod b/go.mod index 8e623038d..49439ba26 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.19 require ( github.com/bytedance/gopkg v0.1.3 github.com/bytedance/sonic v1.15.0 - github.com/cloudwego/gopkg v0.1.11-0.20260226101607-10def4e32fd1 - github.com/cloudwego/netpoll v0.7.2 + github.com/cloudwego/gopkg v0.1.11-0.20260303065100-1e5551ecf390 + github.com/cloudwego/netpoll v0.7.3-0.20260305035010-81277e4f7b67 github.com/fsnotify/fsnotify v1.5.4 github.com/stretchr/testify v1.10.0 github.com/tidwall/gjson v1.14.4 diff --git a/go.sum b/go.sum index e78e93310..b9bd645de 100644 --- a/go.sum +++ b/go.sum @@ -8,10 +8,10 @@ github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCc github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/cloudwego/gopkg v0.1.4/go.mod h1:FQuXsRWRsSqJLsMVd5SYzp8/Z1y5gXKnVvRrWUOsCMI= -github.com/cloudwego/gopkg v0.1.11-0.20260226101607-10def4e32fd1 h1:wg+raHia1H813G0yldWSUb5dzgWccrOEftHct9O7Nvs= -github.com/cloudwego/gopkg v0.1.11-0.20260226101607-10def4e32fd1/go.mod h1:wQv2rXOgrRCYdIrOce+xnAF7MA30CkofQZ3JHZOXY+8= -github.com/cloudwego/netpoll v0.7.2 h1:4qDBGQ6CG2SvEXhZSDxMdtqt/NLDxjAVk0PC/biKiJo= -github.com/cloudwego/netpoll v0.7.2/go.mod h1:PI+YrmyS7cIr0+SD4seJz3Eo3ckkXdu2ZVKBLhURLNU= +github.com/cloudwego/gopkg v0.1.11-0.20260303065100-1e5551ecf390 h1:DERt3Cue/q307RWCd+pPXvzuqmujrrzORgShkeU4Q0s= +github.com/cloudwego/gopkg v0.1.11-0.20260303065100-1e5551ecf390/go.mod h1:wQv2rXOgrRCYdIrOce+xnAF7MA30CkofQZ3JHZOXY+8= +github.com/cloudwego/netpoll v0.7.3-0.20260305035010-81277e4f7b67 h1:0dwPCnAMoeEupEKCAR4paadnmaq39MVdxvmEVlwcu3g= +github.com/cloudwego/netpoll v0.7.3-0.20260305035010-81277e4f7b67/go.mod h1:PI+YrmyS7cIr0+SD4seJz3Eo3ckkXdu2ZVKBLhURLNU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= diff --git a/pkg/app/server/hertz_test.go b/pkg/app/server/hertz_test.go index d27f83b31..d17de55f3 100644 --- a/pkg/app/server/hertz_test.go +++ b/pkg/app/server/hertz_test.go @@ -85,9 +85,8 @@ func TestHertz_Run(t *testing.T) { waitEngineRunning(hertz) hertz.Close() - resp, err := http.Get(fullURL(ln, "/test")) - assert.NotNil(t, err) - assert.Nil(t, resp) + time.Sleep(10 * time.Millisecond) + // Close will not call OnShutdown assert.DeepEqual(t, uint32(0), atomic.LoadUint32(&testint)) } diff --git a/pkg/network/standard/connection.go b/pkg/network/standard/connection.go index d42c7850e..485bca488 100644 --- a/pkg/network/standard/connection.go +++ b/pkg/network/standard/connection.go @@ -26,7 +26,7 @@ import ( "time" "github.com/cloudwego/gopkg/bufiox" - "github.com/cloudwego/gopkg/connstate" + "github.com/cloudwego/gopkg/net/connstate" errs "github.com/cloudwego/hertz/pkg/common/errors" "github.com/cloudwego/hertz/pkg/common/hlog" @@ -38,6 +38,8 @@ type Conn struct { br *bufiox.DefaultReader bw *bufiox.DefaultWriter stater connstate.ConnStater + + buf [8]byte } func (c *Conn) ToHertzError(err error) error { @@ -100,15 +102,13 @@ func (c *Conn) Len() int { } // ReadByte is used to read one byte with advancing the read pointer. -func (c *Conn) ReadByte() (byte, error) { - b, err := c.Peek(1) +func (c *Conn) ReadByte() (b byte, err error) { + // Use Read instead of Peek+Skip to avoid holding a ref to the underlying buffer. + _, err = c.br.Read(c.buf[:1]) if err == nil { - err = c.Skip(1) - } - if err != nil { - return ' ', err + b = c.buf[0] } - return b[0], nil + return } // ReadBinary is used to read next n byte with copy, and the read pointer will be advanced. diff --git a/pkg/network/standard/transport.go b/pkg/network/standard/transport.go index 358ee7d26..b5f5bd0bf 100644 --- a/pkg/network/standard/transport.go +++ b/pkg/network/standard/transport.go @@ -26,7 +26,7 @@ import ( "sync/atomic" "time" - "github.com/cloudwego/gopkg/connstate" + "github.com/cloudwego/gopkg/net/connstate" "github.com/cloudwego/hertz/pkg/common/config" "github.com/cloudwego/hertz/pkg/common/hlog" diff --git a/pkg/network/standard/unix_test.go b/pkg/network/standard/unix_test.go index 39bc21955..b557409f5 100644 --- a/pkg/network/standard/unix_test.go +++ b/pkg/network/standard/unix_test.go @@ -22,7 +22,7 @@ import ( "testing" "time" - "github.com/cloudwego/gopkg/connstate" + "github.com/cloudwego/gopkg/net/connstate" "github.com/cloudwego/hertz/pkg/common/config" ) diff --git a/pkg/protocol/http1/ext/common.go b/pkg/protocol/http1/ext/common.go index 90cd3b982..a2d5e1ff1 100644 --- a/pkg/protocol/http1/ext/common.go +++ b/pkg/protocol/http1/ext/common.go @@ -176,6 +176,12 @@ func appendBodyFixedSize(r network.Reader, dst []byte, n int) ([]byte, error) { } dst = dst[:dstLen] + // Prefer io.Reader over Peek to avoid holding a ref to the underlying buffer. + if rd, ok := r.(io.Reader); ok { + rn, err := io.ReadFull(rd, dst[offset:]) + return dst[:offset+rn], err + } + // Peek can get all data, otherwise it will through error buf, err := r.Peek(n) if err != nil {