反向Proxy项目中如何计算网络传输的Incoming Request总大小
Go反向代理请求全量大小统计方案
实现思路
不需要修改标准库request.go的readRequest函数,直接在TCP连接层做字节计数即可:所有入站请求数据(包含请求行、headers、body、trailers)都是从TCP连接读取的,只需要对net.Conn做一层包装,在读取字节的同时同步累加计数即可,全程无额外解析开销,完全满足高性能要求。
具体实现步骤
- 第一步:实现带计数功能的Conn包装类,满足
net.Conn接口要求
package main import ( "context" "io" "net" "sync/atomic" ) type CountingConn struct { net.Conn bytesRead uint64 } func (c *CountingConn) Read(b []byte) (n int, err error) { n, err = c.Conn.Read(b) atomic.AddUint64(&c.bytesRead, uint64(n)) return n, err } // BytesRead 返回当前连接已读取的总字节数 func (c *CountingConn) BytesRead() uint64 { return atomic.LoadUint64(&c.bytesRead) }
- 第二步:实现自定义Listener,Accept连接时自动包装为计数Conn
type CountingListener struct { net.Listener } func (l *CountingListener) Accept() (net.Conn, error) { rawConn, err := l.Listener.Accept() if err != nil { return nil, err } return &CountingConn{Conn: rawConn}, nil }
- 第三步:在HTTP服务中注入计数逻辑,从请求上下文获取总大小
import "net/http" // 上下文私有key,避免冲突 type connCtxKey struct{} func main() { proxyServer := &http.Server{ // 连接建立时将计数Conn存入上下文 ConnContext: func(ctx context.Context, conn net.Conn) context.Context { return context.WithValue(ctx, connCtxKey{}, conn) }, Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 确保完整读取请求所有内容(包含trailers) _, _ = io.Copy(io.Discard, r.Body) _ = r.Body.Close() // 读取总字节数 countConn := r.Context().Value(connCtxKey{}).(*CountingConn) reqTotalSize := countConn.BytesRead() // 此处插入你的反向代理逻辑,reqTotalSize就是包含所有部分的完整请求大小 }), } rawListener, err := net.Listen("tcp", ":80") if err != nil { panic(err) } // 用自定义计数Listener启动服务 _ = proxyServer.Serve(&CountingListener{Listener: rawListener}) }
注意事项
- 该方案兼容HTTP/1.x、HTTP/2协议,不需要额外适配
- 计数完全和请求读取同步,性能损耗可以忽略,远高于解析后累加字段的方案
- 不建议直接修改标准库
readRequest函数,会导致Go版本升级时需要反复适配,维护成本极高 - 如果需要排除TCP层的ACK等底层包、只统计HTTP协议层的字节,该方案的统计结果完全符合要求
内容的提问来源于stack exchange,提问作者user1270949
相关产品推荐
相关产品推荐

