项目开发缘由:

tengine在接收client端post数据并转发给后端application server进行处理的时候,默认启用buffer模式,也就意味着,当client发送的数据较小时,tengine会将所有post数据保存在内存中之后再转发后端application server,而当client发送的数据较大时(根据配置中所设置的buffer大小来区分数据是否保存到文件),tengine会将post数据保存成临时文件写入磁盘,只有在全部接收完post数据后,才会将临时文件读取回内存并转发后端application server。因此,当访问压力较大且post数据超过buffer大小,那么tengine将会有大量的io操作,从而存在性能风险。
request no buffering的开发就是为了解决该性能问题,其设计思路是,通过 client_body_postpone_sending 配置项设置存储post数据的内存buffer大小,然后通过proxy_request_buffering 和 fastcgi_request_buffering开关来决定是否开启request no buffering功能。当request no buffering开启后,当tengine接收到的post数据大于所设置的buffer大小之后,马上向后端application server转发。这样就规避了之前提到的大量的io操作所带来的性能风险。
性能测试问题:
1. tcp delay所带来的性能瓶颈:
问题描述:
在性能测试的过程中发现,与传统的buffer模式、no buffer size为64k、no buffer size为640k相比,当 no buffer size为8k时,性能表现非常差,用户平均等待时间达到60ms左右,QPS下降100倍。
究其原因:
是因为tcp delay的问题。tcp delay准确的说是Nagle算法,其设计的初衷是为了保证网络性能,因为应用程序可以通过它将任意尺寸的数据放入TCP栈中——即使一次只放一个字节!但是,每个TCP段中都至少装载了40个字节的标记和首部,所以如果TCP发送大量包含少量数据的分组,网络的性能就会严重下降。而Nagle算法试图在发送一个分组之前,将大量TCP数据绑定在一起,鼓励发送全尺寸(LAN上最大尺寸的分组约为1500字节,Internet上是几百字节)的段。这将导致几种HTTP性能问题。首先,小的HTTP报文可能无法填满一个分组,可能会因为等待那些永远不会带来的额外数据而产生时延。其次,Nagle算法与延迟确认之间的交互存在问题——Nagle算法会阻止数据的发送,知道有确认分组抵达为止,但确认分组自身会被延迟确认算法延迟100~200毫秒。
最初方案:
在配置文件中加入 tcp_nodelay on配置项,验证证明无效。原因是该配置项只对tengine的前端生效,不对后端生效。
真正的解决方案:
在代码中加入:
 
当配置文件中tcp_nodelay为on时,对后端也采用tcp_nodelay方式进行数据传输。
C代码 复制代码 收藏代码
  1. if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
  2. ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "upstream tcp_nodelay");
  3.  
  4. tcp_nodelay = 1;
  5.  
  6. if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
  7. (const void *) &tcp_nodelay, sizeof(int)) == -1)
  8. {
  9. ngx_connection_error(c, ngx_socket_errno,
  10. "setsockopt(TCP_NODELAY) failed");
  11. ngx_http_upstream_finalize_request(r, u, 0);
  12. return;
  13. }
  14.  
  15. c->tcp_nodelay = NGX_TCP_NODELAY_SET;
  16. }
if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "upstream tcp_nodelay");

        tcp_nodelay = 1;

        if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
                           (const void *) &tcp_nodelay, sizeof(int)) == -1)
        {
            ngx_connection_error(c, ngx_socket_errno,
                                 "setsockopt(TCP_NODELAY) failed");
            ngx_http_upstream_finalize_request(r, u, 0);
            return;
        }

        c->tcp_nodelay = NGX_TCP_NODELAY_SET;
    }

2. 当tengine的前端与后端均有no delay时的性能问题:
问题描述:
上文讲过,Nagle算法的设计初衷是为了保证网络性能,当tcp nodelay开启后,Nagle算法的防护就失效,当tengine的no buffer size设置为8k(最小为8k,当设置小于8k时,tengine按照buffer默认值8k进行处理)且post数据量大和访问压力大的情况下,tcp以大量小数据块的形式进行传输,导致网络利用率低,存在大量的小包传输。
解决方案:
因为即使no delay存在网络利用率低的问题,但在实际的测试过程中发现,no buffer size设置为8k时的性能表现与其他分组对比,相差不大。因此解决方案建议尽量避免设置no buffer size为8k的情况。暂不做进一步处理。
 
转发请备注转自:100continue.iteye.com。