Go HTTP Server频繁出现http2客户端预读错误的技术问询
Go HTTP2服务器大量preface读取超时错误问题解答
错误日志示例
2022/11/20 13:41:51 http2: server: error reading preface from client AAA:56990: read tcp XXX:8084->AAA:56990: i/o timeout 2022/11/20 13:41:51 http2: server: error reading preface from client BBB:27287: read tcp XXX:8084->BBB:27287: i/o timeout 2022/11/20 13:41:51 http2: server: error reading preface from client CCC:47260: read tcp XXX:8084->CCC:47261: i/o timeout
问题
- 该情况是否正常?是否是服务器配置错误或BUG导致?
- 若属于正常情况,能否对这些客户端提供兼容支持,还是应将其视为故障客户端直接忽略?
- 若属于正常情况,如何仅屏蔽这类错误日志,保留其他类型的日志信息?
解答
1. 情况是否正常?是否是服务器问题?
这种情况属于正常现象,基本不是服务器配置错误或Go HTTP2实现的BUG。
原因是HTTP2协议要求客户端建立连接后,必须先发送特定的"preface"(即PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n)启动会话。如果客户端迟迟不发该内容,或连接建立后直接挂起,服务器就会触发读取超时错误。这类请求通常来自:
- 不符合HTTP2规范的老旧/异常客户端
- 仅建立TCP连接但不完成HTTP握手的网络扫描工具
- 网络不稳定导致的半开连接
2. 兼容支持还是直接忽略?
建议直接忽略这类客户端。
这类请求根本没完成HTTP2初始化握手,服务器无法判断客户端要使用HTTP1还是HTTP2,也没有合规的兼容方式——主动降级到HTTP1不符合协议规范。而且这类请求多为无效流量,继续处理只会浪费服务器资源,Go的HTTP2服务器会自动断开超时连接,无需额外操作。
3. 如何屏蔽这类错误日志?
Go标准库的HTTP2日志通过log包输出,要单独屏蔽这类错误,可通过自定义日志拦截器实现:
- 方法一:自定义
http2.Server的ErrorLog过滤特定错误
Go HTTP2包中,这类超时错误对应http2.ErrReadPrefaceTimeout,可以通过自定义日志记录器过滤:import ( "log" "os" "golang.org/x/net/http2" ) func main() { // 创建自定义日志记录器 customLogger := log.New(os.Stderr, "", log.LstdFlags) filteredLogger := &filteredErrorLogger{logger: customLogger} srv := &http.Server{Addr: ":8084"} h2s := &http2.Server{ ErrorLog: filteredLogger, } http2.ConfigureServer(srv, h2s) // 启动服务器逻辑... } type filteredErrorLogger struct { logger *log.Logger } func (l *filteredErrorLogger) Printf(format string, v ...interface{}) { // 过滤preface读取超时的日志 if format == "http2: server: error reading preface from client %s: %v" { if err, ok := v[1].(error); ok && err.Error() == "read tcp: i/o timeout" { return // 跳过输出 } } // 其他日志正常输出 l.logger.Printf(format, v...) } - 方法二:如果用第三方日志库(如zap、logrus),可在日志采集层过滤包含
error reading preface from client和i/o timeout组合关键词的日志,直接丢弃即可。
内容的提问来源于stack exchange,提问作者Igor Chubin
相关产品推荐
相关产品推荐

