专栏首页开发架构二三事nginx之keepalive与pipeline

nginx之keepalive与pipeline

tcp和http都有keepalive,但是它们的作用是不一样的,tcp 的keepalive是为了确认长连接的状态,而http的keepalive是为了让连接保持得久一些。nginx提供了对keepalive和pipeline的支持。

1. tcp的keepalive机制:

当客户端与服务器建立了tcp连接后,如果客户端一直不发送数据, 或者隔很长时间才发送一次数据。当连接很久没有数据报文传输时,服务器如何去确定对方还在线。到底是掉线了还是确实没有数据传输,连接还需不需要保持,这种情况在TCP协议设计中是需要考虑的。TCP协议通过一种巧妙的方式去解决这个问题,当超过一段时间(tcpkeepalivetime)之后,TCP自动发送一个数据为 空的报文给对方, 如果对方回应了这个报文,说明对方还在线,连接可以继续保持,如果对方没有报文返回并且重试了多次之后则认为连接丢失,没有必要保持连接。这个过程相当于服务器向客户端发送心跳包, 确认客户端是否还在线。对应的内核参数:

echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time2 echo 15 > /proc/sys/net/ipv4/tcp_keepalive_intvl3 echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes

2. http的keepalive机制为:

  • 通常客户端浏览器要展现一张完整的页面需要很多个请求才能完成,如图片,js,CSS等。如果每一个HTTP请求都需要新建并断开一个TCP,这个开销是完全没有必要的。开启HTTP Keep-Alive之后,能复用已有的TCP链接, 当一个请求已经响应完毕,服务器端没有立即关闭TCP连接,而是等待一段时间继续接收浏览器可能发送过来的第二个请求,通常浏览器在第一个请求返回之后会立即发送第二个请求。因此在一个tcp连接上可以存在多个http请求,当然这个如果客户端一直不发送新的http请求,超过一段时间后,nginx服务器还是会关闭这个TCP长连接。
  • 对于http1.0 协议来说,如果响应头中有content-length头,则以content-length的长度就可以知道body的长度,客户端在接收body时,可以依照这个长度接收数据,接收完后,就表示该请求完成。如果没有content-length,客户端会一直接收数据,直到服务端主动端口连接,才表示body接收完。
  • 对于http1.1 协议,如果响应头中transfer-encoding为chunked传输,表示body是流式输出,body被分成多个块,每块的开始会标示出当前块的长度,此时,body不需要指定长度。如果是非chunked传输,而且有Content-length,则按照content-length来接收数据。否则,非chunked且没有content-length,则客户端接收数据,知道服务器主动断开。
  • 如果客户端请求头中connection为close,表示客户端要主动关闭连接。如果connection为keepalive,则表示客户端要保持长连接。请求头中connection的默认值在http1.0中是close,在http 1.1中是默认keepalive。如果要keep-alive,nginx在输出完响应体后,会设置当前连接的keepalive属性,然后等待客户端下一次请求,nginx设置了keepalive的等待最大时间。一般来说,当客户端需要多次访问同一个server时,打开keepalive的效果非常好。
  • http2中已经是全双工模式,不需要使用keepalive来维持长连接,所以在rfc7540规范中移除了这个头 参考:https://httpwg.org/specs/rfc7540.html
  • keepalive并不是免费的午餐,长时间的tcp连接容易导致系统资源无效占用。配置不当的keepalive有时比重复利用连接带来的损失还更大。所以,正确地设置keepalive timeout时间非常重要。

3. 关于pipeline:

在RFC 2616(https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.2.2)中规定了pipelining:

A client that supports persistent connections MAY "pipeline" its requests (i.e., send multiple requests without waiting for each response). A server MUST send its responses to those requests in the same order that the requests were received.Clients which assume persistent connections and pipeline immediately after connection establishment SHOULD be prepared to retry their connection if the first pipelined attempt fails. If a client does such a retry, it MUST NOT pipeline before it knows the connection is persistent. Clients MUST also be prepared to resend their requests if the server closes the connection before sending all of the corresponding responses.Clients SHOULD NOT pipeline requests using non-idempotent methods or non-idempotent sequences of methods (see section 9.1.2). Otherwise, a premature termination of the transport connection could lead to indeterminate results. A client wishing to send a non-idempotent request SHOULD wait to send that request until it has received the response status for the previous request.

讲到这里,有必要把keepalive和pipeline做一个区分: pipeline其实就是流水线作业,它可以看作为keepalive的一种升华,因为pipeline也是基于长连接的,目的就是利用一个连接做多次请求。连接请求头使用keepalive之后,在处理多个请求时,第二个请求要等到第一个请求响应完成才能发起,两个响应时间至少为2RTT。而对于pipeline,客户端请求是打包进行的,第二个请求不必等第一个请求处理完,两个响应的时间可能达到1RTT。http1.1以后是支持pipeline的,关于pipeline的更多细节请自行查阅之前关于Request-Response的推文。

3. nginx对keepalive和pipeline的处理

3.1 对keepalive的处理:

voidngx_http_handler(ngx_http_request_t *r){.........................................        switch (r->headers_in.connection_type) {        case 0://如果版本大于1.0则默认是keepalive            r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);            break;        case NGX_HTTP_CONNECTION_CLOSE://如果指定connection头为close则不需要keepalive            r->keepalive = 0;            break;        case NGX_HTTP_CONNECTION_KEEP_ALIVE:            r->keepalive = 1;            break;        }..................................}static voidngx_http_finalize_connection(ngx_http_request_t *r){    ngx_http_core_loc_conf_t  *clcf;    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);.....................................................................//可以看到如果设置了keepalive,并且timeout大于0,就进入keepalive的处理。    if (!ngx_terminate         && !ngx_exiting         && r->keepalive         && clcf->keepalive_timeout > 0)    {        ngx_http_set_keepalive(r);        return;    } else if (r->lingering_close && clcf->lingering_timeout > 0) {        ngx_http_set_lingering_close(r);        return;    }    ngx_http_close_request(r, 0);}

解析http头部的connection字段后,将会设置TCP的连接类型,是打开长连接还是关闭长连接。然后会设置到请求对象的keepalive中。如果设置了keepalive,并且timeout大于0,就进入keepalive的处理。

3.2 对pipeline的处理

在ngxhttpset_keepalive方法中会相应地进行pipeline的处理:

 hc = r->http_connection;    b = r->header_in;//一般情况下,当解析完header_in之后,pos会设置为last。也就是读取到的数据刚好是一个完整的http请求.当pos小于last,则说明可能是一个pipeline请求。    if (b->pos < b->last) {        /* the pipelined request */        if (b != c->buffer) {        ...

其实nginx的做法很简单,在读取数据时会将读取的数据放到一个buffer里面。当nginx在处理完前一个请求后,如果发现buffer里面还有数据,就认为剩下的数据是下一个请求的开始,然后接下来处理下一个请求(这样也会产生large header)。这就是pipeline请求。

另上nginx对pipeline中的多个请求的处理不是并行的,而是一个接一个的处理,只是在处理第一个请求的时候,客户端就可以发起第二个请求。nginx这样做就可以利用pipeline可以减少从处理完一个请求后到等待第二个请求的请求头数据的时间。参考:https://blog.csdn.net/wenwuge_topsec/article/details/43268343

3.3 nginx的keepalive指令

nginx cookBook page 158-159:

The keepalive directive in the upstream context activates a cache of connections that stay open for each NGINX worker。注意这里的keepalive指令和上面讲的keepalive是有所区别的,keepalive指令是用于为nginx worker缓存连接的。

本文分享自微信公众号 - 开发架构二三事(gh_d6f166e26398),作者:两个小灰象

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • guava cache的一些使用经验

    它本身继承自AbstractMap,实现了ConcurrentMap。是根据jdk1.7中的ConcurrentHashMap中的分段锁的原理来实现的,构造方法...

    开发架构二三事
  • elasticsearch深入搜索一之近似匹配

    1. 从上面几种分词器的对比中可以看出,拼音分词器主要是把中文转换成拼音的方式进行分词; 2. ik_max_word分词和ik_smart分词器主要是索引单词...

    开发架构二三事
  • mysql索引结构与深分页优化

    B-树,这里的 B 表示 balance( 平衡的意思),B-树是一种多路自平衡的搜索树。它类似普通的平衡二叉树,不同的一点是B-树允许每个节点有更多的子节点。

    开发架构二三事
  • 《Learning Scrapy》(中文版)第3章 爬虫基础

    本章非常重要,你可能需要读几遍,或是从中查找解决问题的方法。我们会从如何安装Scrapy讲起,然后在案例中讲解如何编写爬虫。开始之前,说几个注意事项。 因为我们...

    SeanCheney
  • 前端dom操作竟然使得http请求的时间延长了

    最近在项目中遇到了一个奇怪的问题:在google浏览器的调试窗口network下看到一个请求的时间一直是2s多,但是当我把这个请求单独拿出来执行的时候发现根本用...

    Theone67
  • 网络编程之HTTP请求报文和HTTP响应报文

    HTTP报文是面向文本的,报文中的每一个字段都是一些ASCII码串,各个字段的长度是不确定的。HTTP有两类报文:请求报文和响应报文。

    lyb-geek
  • 几张趣图助你理解HTTP状态码~

    HTTP状态码(图一): ? 注释: 301—永久移动。被请求的资源已被永久移动位置; 302—请求的资源现在临时从不同的 URI 响应请求; 305—使...

    编程范 源代码公司
  • HTTP状态码大全

    写在前面 我们在开发Web服务的时候,经常会遇到404,500等错误。对于初学者来说遇到错误,不知如何下手,今天我们来看看每个状态都表示什么意思? HTTP状...

    互扯程序
  • 基于Django的电子商务网站开发(连载5)

    HTTP的请求方式共分为OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE和CONNECT八种(注意:这些方法均为大写),其中比较常用...

    小老鼠
  • 几张趣图助你理解HTTP状态码~

    HTTP状态码(图一): ? 注释: 301—永久移动。被请求的资源已被永久移动位置; 302—请求的资源现在临时从不同的 URI 响应请求; 305—使...

    顶级程序员

扫码关注云+社区

领取腾讯云代金券