Fasthttp是一个高性能的web server框架。Golang官方的net/http性能相比fasthttp逊色很多。根据测试,fasthttp的性能可以达到net/http的10倍。所以,在一些高并发的项目中,我们经常用fasthttp来代替net/http。但是,这高并发性能的背后是以牺牲请求数据完整性为惨痛代价换来的,具体我们可以看以下例子。
1、编写一个简单的fasthttp server
fasthttp提供的api很容易让我们创建一个httpserver。下图是创建好的一个http server,该server接收post后缀的http请求。httpHandle用来处理该请求的业务逻辑,这里我们暂时省略具体的业务逻辑,只是输出请求体。
2、获取request body
httpHandle方法接受的是一个RequestCtx的对象,通过ctx可以获取到request的body。获取到body我们可以解析body进行后续业务逻辑处理。平常我们用的最多的是以json字符串来传递request body,那在这里就可以进行反序列化得到request对象。
3、错乱的request body
如果这个web server是一个高并发的server,那这里的取到的request body就会有两种情况。(偶发性)
正常接收到的request body json字符串如下图:
1、本次请求的body丢失一部分数据
2、本次请求携带了上一次请求的部分数据
感兴趣的同学可以在这里进行模拟并发请求操作,这不是一个必现的问题,但是当你的并发请求高的时候(我的业务中QPS=5000),这个问题就会大面积出现。
4、复用惹的祸
为什么会出现上面的问题?这个问题在golang官方的net/http下是不存在的。问题的根源就在这个RequestCtx。
上图是fasthttp获取RequestCtx对象的代码,我们可以看出fasthttp使用了一个ctxPool来维护RequestCtx,每次请求都先去ctxPool中获取。如果能获取到就用池中已经存在的,如果获取不到,new出一个新的RequestCtx。这也就是fasthttp性能高的一个主要原因,复用RequestCtx可以减少创建对象所有的时间以及减少内存使用率。但是随之而来的问题是:如果在高并发的场景下,如果整个请求链路中有另起的goroutine,前一个RequestCtx处理完成业务逻辑以后(另起的协程还没有完成),立刻被第二个请求使用,那就会发生前文所述的错乱的request body。
5、复制新的request
我目前是通过复制一个新的request来解决这个问题。
每次请求进来以后,新创建一个request的对象,然后将RequestContext对象中的Request复制到新建的request对象中,后续的逻辑处理就用新的request对象。大家如果有其它的方案可以一起讨论。
总之,fasthttp可以很大程度提升系统的性能,但是,复用导致的数据错乱问题,在平常的业务使用中还需注意。
领取专属 10元无门槛券
私享最新 技术干货