fasthttp:高性能背后的惨痛代价

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可以很大程度提升系统的性能,但是,复用导致的数据错乱问题,在平常的业务使用中还需注意。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20191029A0A4RE00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券