Fasthttp的body问题
fasthttp的body问题
0 前言
本人水平有限,共同探讨,不足之处欢迎指正
1 介绍
FastHTTP
是一个高性能的Web服务器框架。与Golang官方的net/http
相比,fasthttp
的性能明显更佳。官方资料显示,fasthttp
的性能是net/http
的10倍左右。因此,在需要处理高并发请求的项目中,我们通常会使用fasthttp
替代net/http
。
2 问题
有一段时间,线上服务经常重启。检查了重启前的日志,发现基本都是由于JSON反序列化失败引起的。进一步排查后,发现问题出在fasthttp上。
2.1 问题复现
FastHTTP提供的API让我们很容易创建一个HTTP服务器。下图显示了一个已创建的HTTP服务器。HTTPHandle用于处理该请求的业务逻辑,这里我们暂不考虑具体的业务逻辑,仅输出请求体。
httpHandle方法接受一个RequestCtx对象作为参数。通过该对象的ctx,可以获取请求的body。解析body后,我们可以进行后续的业务逻辑处理。通常,请求体是以JSON字符串的形式传递的,因此在这里可以进行反序列化,得到对应的请求对象。
如果这个Web服务器并发较高,那么在获取到的 request body 中除了正常情况外,还可能出现本次请求的数据丢失一部分或包含上一次请求数据的情况。这些情况可能导致 JSON 反序列化失败。(偶发性)
2.2 问题原因
为什么会出现这个问题?在golang的net/http
库中并不存在类似的问题,根源在于这个RequestCtx
。
上图展示了FastHTTP获取RequestCtx
对象的代码。FastHTTP通过一个ctxPool
来维护RequestCtx
对象,每次请求时首先尝试从ctxPool
中获取。如果池中有可用的RequestCtx
,则直接使用;如果池中没有,则创建一个新的。这种复用RequestCtx
的方式是FastHTTP性能高的主要原因之一,因为它减少了创建对象所需的时间,也降低了内存使用率。然而,在高并发场景下,如果处理请求的协程中另有子协程启动,那么前一个RequestCtx
处理完成业务逻辑时,如果后一个请求立即使用该RequestCtx
,就可能会出现之前提到的请求体混乱问题。
2.3 问题解决方案
复制整个Request对象
每次请求都会复制一个新的 fasthttp.Request
对象。
使用 Acquire/Release 控制对象生命周期
fasthttp
提供了内置的 AcquireRequest
和 ReleaseRequest
方法,用于从对象池中获取和归还对象。通过这些方法,可以手动控制内存复用,这种方式更贴近 fasthttp
的内部机制,既能保证性能,又能确保安全性。
3 总结
fasthttp可以显著提升系统的性能,但在业务使用中,需要注意复用可能导致的数据错乱问题。