目录
1. HTTP长连接原理
HTTP属于 TCP/IP模型中的应用层协议,HTTP长连接和HTTP短连接,指的是传输层的TCP连接是否被多次使用。
一般来说,用户通过浏览器输入URL回车,浏览器会通过DNS解析域名得到服务器的IP地址,然后通过解析出来的 IP和URL中的端口(默认为 80)发起建立 TCP连接请求,通过三次握手之后,建立TCP连接。
1.1 HTTP长连接和短连接
默认情况下,HTTP的1.0版本协议中,HTTP在每次请求结束后都会主动释放TCP连 接,因此HTTP连接是一种“短连接”。客户端与服务端通过HTTP短连接的交互过程,具体如下图所示。
在高并发场景使用HTTP“短连接”通信,会出现两个问题:
(1)性能较差:传输层的TCP连接不会复用,每一次请求,都需要建立和拆除一次 TCP连接,也即是说,每次请求均需要TCP三次握手建立连接,TCP四次挥手关闭连接,性 能较差。
(2)很容易出现端口被占满:在主动断开方,系统会出现大量的TIME_WAIT状态的 TCP连接,只有等2个MSL后,TCP连接才会进行关闭掉,在高并发场景中,如果服务器主动断开连接,则很容易发生端口耗尽。当然,如果连接被设置了SO_RESUSEADDR特性,其端口可能被其他连接复用,尽管如此,还是会存在不少的约束条件影响到端口复用。出于以上两个原因,在高并发场景使用HTTP“短连接”进行通信肯定是不行的。
HTTP长连接,也叫HTTP持久连接,指的是TCP连接建立后,该传输层连接不再进行释放,供应用层反复使用。客户端与服务端通过HTTP长连接的交互过程,具体如下图所示:
HTTP长连接的特点是:
(1)性能较高,不需要重复建立TCP连接或者关闭TCP连接;
(2)TCP数据传输连接基本上不会出现CLOSE_WAIT和TIME_WAIT的问题,系统资源的使用效率会大大提升。 HTTP长连接也有缺点:一般需要一个连接池来对可供复用的TCP长连接进行管理和监测。常见的数据库连接池、HTTP连接池,本质上都属于TCP连接池。
1.2 HTTP/1.1 协议支持的长连接
HTTP/1.1默认使用长连接而不是短连接,除非显式关闭TCP连接。如果要显式关闭连 接,需要在HTTP报文首部加上“Connection:Close”请求头,也就是说在HTTP/1.1协议 中,默认情况下,所有的TCP连接都可以进行复用的。
当然,不发送“Connection:Close”请求头,不意味着服务器承诺TCP连接永远保持打开。空闲的TCP连接也可以被客户端与服务端关闭。
2. 服务端HTTP长连接技术
本节对主流的反向代理服务器Nginx和应用服务器Tomcat的服务端长连接配置进行介绍。
2.1 Tomcat的长连接配置
生产环境所用的Java应用服务器不一定是Tomcat,可能是JBoss、Jetty或者其他的应用 服务器。无论使用哪一种服务器,其HTTP长连接配置的原理是类似的,所以,这里以 Tomcat为例进行应用服务器的长连接配置介绍。服务器端Tomcat的长连接配置,主要分为两种场景:
(1)独立部署的Tomcat
在传统的Nginx+Tomcat架构的Web应用中,一般使用独立部署的Tomcat作为Web服务器。
(2)内嵌部署的Tomcat
在目前主流的Spring Boot应用,一般使用内嵌的Tomcat作为Web服务器。
以上两种细分场景的Tomcat使用,具体如下图所示:
图:Tomcat使用的两种细分场景
1. 独立部署 Tomcat 的长连接配置
针对于细分场景一中的独立部署Tomcat,其长连接配置是通过修改Tomcat配置文件中 Connector(连接器)的配置完成的。一个使用HTTP长连接的Connector连接器的配置示例大致如下(Tomcat版本假定8.0或以上):
<Connector port="8080" redirectPort="8443"
protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
URIEncoding="UTF-8"
keepAliveTimeout="15000"
maxKeepAliveRequests="-1"
maxConnections="3000"
maxThreads="1000"
maxIdleTime="300000"
minSpareThreads="200"
acceptCount=”100″
enableLookups="false" />
对以上配置示例中用到的三个长连接配置选项,介绍如下:
(1)keepAliveTimeout
此选项为TCP连接保持时长,单位为毫秒。表示在下次请求过来之前,该连接将被Tomcat保持多久。在keepAliveTimeout时间范围内,假如客户端不断有新的请求过来,则该连接将一直被保持。KeepAliveTimeout选项决定一个不活跃的连接能保持多少时间。
(2)maxKeepAliveRequests
此选项表示长连接最大支持的请求数。超过该请求数的连接将被关闭,关闭的时候 Tomcat会返回一个带“Connection: close”响应头的给客户端。 当maxKeepAliveRequests的值为-1时,表示没有最大请求数限制;如果其值被设置为 1,将会禁用掉HTTP长连接。
默认情况下Tomcat是使用长连接的,如果要关闭长连接,只要将maxKeepAliveRequests设置为1即可。
(3)maxConnections
Tomcat在任意时刻能接收和处理的最大连接数。如果其值被设置为-1,则连接数不受限制。由于Linux的内核默认限制了单进程最大打开文件句柄数为1024,因此,如果此配置 项的值超过1024,则相应的需要对Linux系统的单进程最大打开文件句柄数限制进行修改。
以上是对Tomcat的HTTP长连接配置选项的介绍。总的来说,使用长连接能提高服务性 能,不过,如果使用不当,也会带来一些不利的结果。
使用长连接意味着,一个TCP连接在当前请求结束后,如果没有新的请求到来, Socket连接不会立马释放,而是等keepAliveTimeout到期之后才被释放,如果一个高负载的 Tomcat服务器建立的很多长连接,将无法继续建立新的连接,无法为新的客户端提供服 务。所以,对于Tomcat长连接的配置需要慎重,错误的参数可能导致严重的性能问题,需要根据具体的负载,配置合适的KeepAliveTimeout和MaxKeepAliveRequests的选项值。
2. 内嵌式部署 Tomcat 的长连接配置
针对于细分场景二中的内嵌式Tomcat,其长连接配置可以通过一个自动配置类完成。 在自动配置类中,可以配置一个TomcatServletWebServerFactory容器工厂Bean实例, SpringBoot将通过该工厂实例,在运行时获取内嵌式Tomcat容器实例。在容器工厂配置代 码中,可以对Tomcat的Connector的三个长连接相关属性进行具体的配置。
一段简单的定制化TomcatServletWebServerFactory容器工厂的配置代码大致如下:
package com.crazymaker.springcloud.standard.config;
//....省略 import
@Configuration
@ConditionalOnClass({Connector.class})
public class TomcatConfig<HttpConnectionProperties> {
@Autowired
private HttpConnectionProperties httpConnectionProperties;
@Bean
public TomcatServletWebServerFactory
createEmbeddedServletContainerFactory() {
TomcatServletWebServerFactory tomcatFactory =
new TomcatServletWebServerFactory();
//增加连接器的定制配置
tomcatFactory.addConnectorCustomizers(connector -> {
Http11NioProtocol protocol =
(Http11NioProtocol) connector.getProtocolHandler();
// 定制 keepAliveTimeout,确定下次请求过来之前 Socket 连接保持多久
// 设置 600 秒内没有请求则服务端自动断开 socket 连接
protocol.setKeepAliveTimeout(600000);
// 当客户端发送超过 10000 个请求,强制关闭掉 socket 连接
protocol.setMaxKeepAliveRequests(1000);
//设置最大连接数
protocol.setMaxConnections(3000);
//...省略其他配置
});
return tomcatFactory;
}
}
以上示例是SpringBoot2.0.8中的内嵌式Tomcat长连接配置,具体的三个配置选项的语义和独立Tomcat的配置是相同的,仅仅是形式上的不同。
2.2 Nginx承担服务端角色时的长连接设置
无论在传统的Nginx+Tomcat架构中,还是在目前主流的Nginx+SpringCloud架构中,反 向代理Nginx都承担了两种角色:对于下游客户端来说Nginx承担了服务端角色,对于上游 的WEB服务来说Nginx承担了客户端角色。Nginx承担的两种角色,具体如下图所示:
Nginx承担服务端角色时的长连接,主要通过keepalive_timeout和keepalive_requests两个指令完成相关设置。一段简单的Nginx承担服务端角色时的长连接配置代码,大致如下
#...
http{
include mime.types;
default_type application/octet-stream;
#长连接保持时长
keepalive_timeout 65s;
#长连接最大处理请求数
keepalive_requests 1000;
#...
server{
listen 80;
server_name openresty localhost;
#长连接保持时长
keepalive_timeout 10s;
#长连接最大处理请求数
keepalive_requests 10;
location/{
root html;
index index.html index.htm;
}
#...
}
}
对以上配置代码中涉及的两个长连接相关指令,具体介绍如下:
(1) keepalive_requests
此指令设置同一个长连接可以处理的最大请求数,请求数超过此值,长连接将关闭。 其格式如下:
语法:keepalive_requests number
默认值:keepalive_requests 100
上下文:http、server、location
keepalive_requests指令用于设置一个长连接上可以服务的最大请求数量,当最大请求 数量达到时,长连接将被关闭,Nginx中其默认值是100。一个长连接建立之后,Nginx就会为这个连接设置一个计数器,记录这个长连接上已经接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时,则Nginx会强行关闭这个长连接,逼迫客户端不得不重新建立新的长连接。
(2)keepalive_timeout
此指令用于设置长连接的空闲保持时长,表示在下次请求过来之前,该连接将被Nginx 保持多久。在keepalive_timeout时间范围内,假如客户端不断有新的请求过来,则该连接将 一直被保持。
语法:keepalive_timeout timeout [header_timeout];
默认值:keepalive_timeout 60s;
上下文:http、server、location
keepalive_timeout指令的第一个参数,用于设置客户端的长连接在服务器端保持的最长 时间(默认60秒),如果值设置为0,会禁用HTTP长连接。对于一些并发量较高的内部服务器通讯的场景,其值可以适当加大,比如增加到120秒甚至300秒。
keepalive_timeout指令的第二个参数,是一个可选参数,其作用为HTTP响应报文增加 一个“Keep-Alive: timeout=time”头部选项,用于告知客户端长连接的保持时间,通常可 以不用设置。该响应头可以被Mozilla 浏览器识别和处理,Mozilla浏览器会在timeout空闲 时间之后,关闭TCP长连接;而MSIE浏览器则在大约60秒后会关闭长连接。
2.3 服务端长连接设置的注意事项
在进行服务端长连接设置时,keepalive_timeout和keepalive_requests的值,并不是越大越好,而是要根据具体场景而定。
场景一:单个客户端的 HTTP 请求数较少时
比如在客户端是普通用户时,客户端是网页浏览器,当用户通过浏览器在访问服务端 时,其单个用户的请求数是比较有限的,1分钟之内所发出的请求数之多在百位数左右。在这种场景下,如果Nginx的服务端长连接设置如下:
#长连接保持时长
keepalive_timeout 65s;
#长连接最大处理请求数
keepalive_requests 1000;
上述设置会导致大量的长连接由于请求数达不到1000,一直在空闲等待,需要等到65秒结束 之后才被关闭,造成服务器资源的浪费。所以,需要减少长连接最大处理请求数和长连接 保持时长,初步优化后的配置大致如下:
#长连接保持时长
keepalive_timeout 10s;
#长连接最大处理请求数
keepalive_requests 100;
但是,如果配置得极端,将长连接最大处理请求数减小得太多,可能会导致另外的问 题。比如,将长连接最大处理请求数减到10,其配置如下:
#长连接保持时长
keepalive_timeout 10s;
#长连接最大处理请求数
keepalive_requests 10;
当QPS=10000时,假定一共100个用户,单个客户端每秒发出100个请求。由于以上配 置中每个连接只能最多处理10次请求,单个客户端每秒发出100个请求相当于每个用户需要10个连接,在总体100个用户的情况下,意味着平均每秒钟就会有1000个长连接将被Nginx主动关闭。在这个情况下,了解前面介绍的TCP连接四次挥手知识的读者就会知道,服务端Nginx就会有大量的TIME_WAIT的Socket连接。
所以,keepalive_requests的值,不能比单个客户端在keepalive_timeout时间范围的实际请求数少太多,如果少太多,在QPS较高的场景,会出现大量连接被服务端主动关闭而出现大量TIME_WAIT连接。
当然,keepalive_requests的值,也不能比单个客户端在keepalive_timeout时间范围实际请求数多太多,这样会导致大量的TCP长连接出现空闲等待。
总体而言,keepalive_requests的值与单客户端在keepalive_timeout时间范围的实际请求 数量,要做到基本的匹配。
场景二:单个客户端的请求数较多时
比如在客户端不是普通用户,而是下游的代理服务器。在这种场景下,客户端数量是 很少的,而单个客户端与服务器之间的请求数是非常多的。
这种场景的设置比较简单,可以尽可能的对长连接进行复用,keepalive_requests值可 以设置偏大,示例的配置如下:
#长连接保持时长
keepalive_timeout 65s;
#长连接最大处理请求数
keepalive_requests 100000;
当然,在此场景中,选项keepalive_timeout可以配置一个较大的值。但是,对于Nginx 来说,不能对单个连接的处理请求数不做限制,必须定期关闭连接,才能释放每个连接的所分配的内存。由于使用过大请求数可能会导致内存占用过度,因此不建议为 keepalive_requests设置太大的值,当然更不能不做keepalive_requests设置。
无论是Nginx、Tomcat还是其他的服务器,有关服务端长连接的设置,其原理是类似 的,仅仅是具体参数的命名规则不同,或者是配置形式稍微有点不同。
参考资料:《Java高并发核心编程 卷1:NIO、Netty、Redis、ZooKeeper》
今天的文章简述HTTP协议的工作原理_https与http区别分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:http://bianchenghao.cn/78844.html