您的位置:首页 > 理论基础 > 计算机网络

tcp_nodelay(1)

2016-01-12 09:39 211 查看
转发请备注转自:100continue.iteye.com


项目开发缘由:

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大小时(如请求文件大小为10K,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代码  


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算法的防护就失效,当请求文件大小稍大于no buffer size大小时(如请求文件大小为10K,no buffer size为8K)且访问压力大的情况下,tcp以大量小数据块的形式进行传输,导致网络利用率低,存在大量的小包传输。

解决方案:

应用方需要权衡线上上传数据的size范围,服务器硬件性能等指标,选择合理的buffer size配置,避免出现大量上传请求的数据size在buffer size范围左右。并启用监控策略,监控显示数据流量变化情况。


(2)

今天在用nginx作web缓存的时候,发现在http里加入这样个参数,能有效的提高数据的实时响应性,那就是tcp_nodelay.下面我们来说说tcp_nodelay的原理:

       TCP_NODELAY和TCP_CORK基本上控制了包的“Nagle化”,这里我们主要讲TCP_NODELAY.Nagle化在这里的含义是采用Nagle算法把较小的包组装为更大的帧。JohnNagle是Nagle算法的发明人,后者就是用他的名字来命名的,他在1984年首次用这种方法来尝试解决福特汽车公司的网络拥塞问题(欲了解详情请参看IETF RFC 896)。他解决的问题就是所谓的silly window syndrome,中文称“愚蠢窗口症候群”,具体含义是,因为普遍终端应用程序每产生一次击键操作就会发送一个包,而典型情况下一个包会拥有一个字节的数据载荷以及40个字节长的包头,于是产生4000%的过载,很轻易地就能令网络发生拥塞,。Nagle化后来成了一种标准并且立即在因特网上得以实现。它现在已经成为缺省配置了,但在我们看来,有些场合下把这一选项关掉也是合乎需要的。 

      现在让我们假设某个应用程序发出了一个请求,希望发送小块数据,比如sns游戏中的点击确定按钮。我们可以选择立即发送数据或者等待产生更多的数据然后再一次发送两种策略。如果我们马上发送数据,那么交互性的以及客户/服务器型的应用程序将极大地受益。例如,当我们正在发送一个较短的请求并且等候较大的响应时,相关过载与传输的数据总量相比就会比较低,而且,如果请求立即发出那么响应时间也会快一些。以上操作可以通过设置套接字的TCP_NODELAY选项来完成,这样就禁用了Nagle算法,在nginx中设置tcp_nodelay
on,注意放在http标签里。

      另外一种情况则需要我们等到数据量达到最大时才通过网络一次发送全部数据,这种数据传输方式有益于大量数据的通信性能,典型的应用就是文件服务器。应用Nagle算法在这种情况下就会产生问题。但是,如果你正在发送大量数据,你可以设置TCP_CORK选项禁用Nagle化,其方式正好同TCP_NODELAY相反(TCP_CORK 和 TCP_NODELAY 是互相排斥的)。下面就让我们仔细分析下其工作原理。 

       假设应用程序使用sendfile()函数来转移大量数据(nginx里可以设置sendfile on)。应用协议通常要求发送某些信息来预先解释数据,这些信息其实就是报头内容。典型情况下报头很小,而且套接字上设置了TCP_NODELAY。有报头的包将被立即传输,在某些情况下(取决于内部的包计数器),因为这个包成功地被对方收到后需要请求对方确认。这样,大量数据的传输就会被推迟而且产生了不必要的网络流量交换。 

但是,如果我们在套接字上设置了TCP_CORK(可以比喻为在管道上插入“塞子”)选项,具有报头的包就会填补大量的数据,所有的数据都根据大小自动地通过包传输出去。当数据传输完成时,最好取消TCP_CORK选项设置给连接“拔去塞子”以便任一部分的帧都能发送出去。这同“塞住”网络连接同等重要。 

总而言之,如果你肯定能一起发送多个数据集合(例如HTTP响应的头和正文),那么我们建议你设置TCP_CORK选项,这样在这些数据之间不存在延迟。能极大地有益于WWW、FTP以及文件服务器的性能,同时也简化了你的工作.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: