Keep-Alive下Http如何区分多个请求

使客户端到服务器端的链接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了创建或者从新创建链接。
HTTP/1.0中默认使用Connection:close, 在HTTP/1.1中已经默认使用Connection: keep-alive。TCP协议层默认并不开启KeepAlive功能。
java

用法

http1.1中因默认支持长链接,因此若是不但愿使用则须要在header中指明connection的值为close;而server也不想支持,则在response中也须要明确说明connection的值为close。
HTTP Connection的 close设置容许客户端或服务器中任何一方关闭底层的链接时双方都会要求在处理请求后关闭它们的TCP链接。即不管request仍是response的header中包含了值为close的connection,都代表当前正在使用的tcp连接在请求处理完毕后会被断掉,之后client再进行新的请求时就必须建立新的tcp连接了。(程序中能够在过滤器中加入:response.setHeader("connection", "close");)
浏览器在请求头部添加 Connection:keep-alive,以此告知服务器本身支持长链接方式,而假若服务器也支持,那么就在响应头部添加 Connection:keep-alive,从而告诉浏览器长链接。服务器还能够经过 Keep-Alive:timeout=10, max=100 的头部告诉浏览器“10 秒算超时时间,最长不能超过 100 秒”。 客户端和服务端均可以设置timeout和max属性值,但http链接保持时间是由服务端的消息头connection字段和keep-alive字段定的linux

//org.apache.http.impl.execchain.MainClientExec#execute
......
 
//从链接池中lease connection
final HttpClientConnectionmanagedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS);
 
......
 
//将conenction封装在ConnectionHolder中
final ConnectionHolder connHolder = new ConnectionHolder(this.log, this.connManager, managedConn);
 
......
 
// The connection is in or can be brought to a re-usable state.
//若是返回值消息头中connection设置为close,则返回false
if (reuseStrategy.keepAlive(response, context)) {
    // Set the idle duration of this connection
    //取出response消息头中,keep-alive的timeout值
    final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
    if (this.log.isDebugEnabled()) {
        final String s;
        if (duration > 0) {
            s = "for " + duration + " " + TimeUnit.MILLISECONDS;
        } else {
            s = "indefinitely";
        }
        this.log.debug("Connection can be kept alive " + s);
    }
    //设置失效时间
    connHolder.setValidFor(duration, TimeUnit.MILLISECONDS);
    connHolder.markReusable();
} else {
    connHolder.markNonReusable();
}

TCP KeepAlive与 Http keep-alive 区别

http keep-alive 意图在于链接复用。是为了让TCP存活更久以便复用TCP链接,在一个TCP链接上进行屡次的HTTP请求从而提升性能。
tcp
KeepAlive是TCP的一种检测TCP链接情况的保鲜机制, 意图在于保活、心跳,检测链接错误。
TCP通常而言为服务器端提供保活功能: 若是客户端已经消失,使得服务器上保留一个半开放的链接,而服务器又在等待来自客户端的数据,则服务器将永远等待客户端的数据, 保活功能就是试图在服务器端检测到这种半开放的链接。nginx

保活心跳机制

及时有效地检测到一方的非正常断开,保证链接的资源被有效利用。apache

  1. 自实现
    大体方法是:服务器在一个 Timer事件中定时向客户端发送一个短小精悍的数据包,而后启动一个低级别的线程,在该线程中不断检测客户端的回应, 若是在必定时间内没有收到客户端的回应,即认为客户端已经掉线;一样,若是客户端在必定时间内没有收到服务器的心跳包,则认为链接不可用。
  2. Keep-Alive
    keepalive只能检测链接是否存活,不能检测链接是否可用。eg. 服务器由于负载太高致使没法响应请求可是链接仍然存在,此时keepalive没法判断链接是否可用。
    • HTTP保活:
      Httpd守护进程,通常都提供了keep-alive timeout时间设置参数。eg:  nginxkeepalive_timeout,和ApacheKeepAliveTimeout
      一个http产生的tcp链接在传送完最后一个响应后,还须要hold住 keepalive_timeout秒后,若是守护进程在这个等待的时间里,一直没有收到浏览器发过来http请求,则关闭这个http链接。
      Tomcat中的相关设置,在conf/server.xml 中的Connector 元素中:
      keepAliveTimeout:
      The number of milliseconds this Connector will wait for another HTTP request before closing the connection. The default value is to use the value that has been set for the connectionTimeout attribute.
      
      maxKeepAliveRequests:
      The maximum number of HTTP requests which can be pipelined until the connection is closed by the server. Setting this attribute to 1 will disable HTTP/1.0 keep-alive, as well as HTTP/1.1 keep-alive and pipelining. Setting this to -1 will allow an unlimited amount of pipelined or keep-alive HTTP requests. If not specified, this attribute is set to 100.
    • TCP保活:   
      没有一个全局的选项去开启TCP的KeepAlive,可在TCP的socket中经过setsockopt系统调用针对单独的socket进行设置开启(使用时须要#include <netinet/tcp.h>, 不然SOL_TCP和TCP_KEEPIDLE等3个宏找不到)。
      int keepAlive = 1; 	 // 开启keepalive属性. 缺省值: 0(关闭)
      int keepIdle = 60;	 // 若是在60秒内没有任何数据交互,则进行探测. 缺省值:7200(s)
      int keepInterval = 5;	// 探测时发探测包的时间间隔为5秒. 缺省值:75(s)
      int keepCount = 2;	 // 探测重试的次数. 所有超时则认定链接失效..缺省值:9(次)
      setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));
      setsockopt(s, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));
      setsockopt(s, SOL_TCP, TCP_KEEPINTVL, (void*)&keepInterval, sizeof(keepInterval));
      setsockopt(s, SOL_TCP, TCP_KEEPCNT, (void*)&keepCount, sizeof(keepCount));
      // TCP_KEEPCNT : 覆盖 tcp_keepalive_probes ;
      // TCP_KEEPIDLE : 覆盖tcp_keepalive_time ;
      // TCP_KEEPINTVL : 覆盖 tcp_keepalive_intvl int keepAlive = 1;
      • 在windows系统中能够经过修改注册表等来达到调整:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
        • KeepAliveTime: 默认7,200,000毫秒(两个小时)。若是这段时间内没有活动,则会发送保持活动信号。默认状况下不发送保活数据包。
          若是须要对丢失接收方敏感,更快地发现丢失了接收方,须要考虑减少这个值。若是长期不活动的空闲链接出现次数较多,而丢失接收方的状况出现较少,可能会要提升该值以减小开销。缺省状况下,若是空闲链接 7200000 毫秒(2 小时)内没有活动,Windows 就发送保持活动的消息。一般,1800000 毫秒是首选值,从而一半的已关闭链接会在 30 分钟内被检测到。 
        • KeepAliveInterval:默认1000毫秒(1 秒)。 肯定在收到响应以前,保活重传之间的时间间隔。
          一旦收到一个响应,将由 KeepAliveTime 值从新控制在下一次保活传输以前的延迟。若是通过 TcpMaxDataRetransmissions 指定的从新传输次数后仍无响应,将放弃链接。 
          缺省状况下,在未收到响应而从新发送保持活动的消息以前,Windows 会等待 1000 毫秒(1 秒)。若是指望较长的响应时间,须要提升该值以减小开销。若是须要减小花在验证接收方是否已丢失上的时间,请考虑减少该值或 TcpMaxDataRetransmissions 值。
      •  在linux中:三个内核参数:tcp_keepalive_time(开启keepalive的闲置时长)tcp_keepalive_intvl(keepalive探测包的发送间隔)和 tcp_keepalive_probes (若是不予应答,探测包的发送次数)。
        修改配置文件, 对整个系统全部的socket有效。(在系统重启后三个参数的值又会恢复到默认值)
        #cat /proc/sys/net/ipv4/tcp_keepalive_time  7200  
        
        #cat /proc/sys/net/ipv4/tcp_keepalive_intvl  75  
        
        #cat /proc/sys/net/ipv4/tcp_keepalive_probes  9
        
        
        
        #echo 60 > /proc/sys/net/ipv4/tcp_keepalive_time  
        
        #echo 5 > /proc/sys/net/ipv4/tcp_keepalive_intvl  
        
        #echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes
        全局设置可更改/etc/sysctl.conf, 使用 sudo sysctl -p当即生效;(永久有效)
        net.ipv4.tcp_keepalive_intvl = 20
        net.ipv4.tcp_keepalive_probes = 3
        net.ipv4.tcp_keepalive_time = 60

HTTP如何区分多个请求

Content-Length

表示实体内容的长度。浏览器经过这个字段来判断当前请求的数据是否已经所有接收。c#

因此,当浏览器请求的是一个静态资源时,即服务器能明确知道返回内容的长度时,能够设置Content-Length来控制请求的结束。但当服务器并不知道请求结果的长度时,如一个动态的页面或者数据,Content-Length就没法解决上面的问题,这个时候就须要用到Transfer-Encoding字段。windows

Transfer-Encoding

表示传输编码。
还有一个相似的字段叫作:Content-Encoding。区别是Content-Encoding用于对实体内容的压缩编码Content-Encoding: gzip; Transfer-Encoding则改变了报文的格式。
当服务端没法知道实体内容的长度时,可指定Transfer-Encoding:chunked (还可同时指定Transfer-Encoding: gzip),代表实体内容数据不只是gzip压缩的,仍是分块传递的。最终当浏览器接收到一个长度为0的chunked时, 标识当前请求内容已所有接收。浏览器

chunked

分块编码, 标识将数据分红一块一块的发出。Chunked编码将使用若干个Chunk串连而成,由一个标明长度为0 的chunk标示结束
chunk-size指定十六进制的数字表明后面chunk-data的字节长度,若是是“0”,则表示chunk-size为0,该chunk为last-chunk,无chunk-data部分。服务器

Chunked-Body   = *chunk            //0至多个chunk

last-chunk         //最后一个chunk

trailer            //尾部

CRLF               //结束标记符

chunk          = chunk-size [ chunk-extension ] CRLF   chunk-data CRLF // 单个chunk内容

chunk-size     = 1*HEX

last-chunk     = 1*("0") [ chunk-extension ] CRLF

chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )

chunk-ext-name = token

chunk-ext-val  = token | quoted-string

chunk-data     = chunk-size(OCTET)

trailer        = *(entity-header CRLF)

总结

一、Content-Length若是存在而且有效的话,则必须和消息内容的传输长度彻底一致。(若是太短则会截断,过长则会致使超时)socket

二、若是存在Transfer-Encoding(重点是chunked),则在header中的Content-Length会被忽视。tcp

三、若是采用短链接,则直接能够经过服务器关闭链接来肯定消息的传输长度。

引贴: Windows系统下的TCP参数优化
          HTTP长链接、短链接使用及测试

相关文章
相关标签/搜索