http1.1中引入了keep-alive机制,使得http协议连接能够延迟关闭,进行复用,能够进行一次3挥手,而完成多次进行通信。
更为重要的是下面问题中的一种场景:减少打开关闭连接次数,显著减少处于time_wait的TCP连接数,提高瞬时系统并发能力。
实验方法:
一共进行3次不同的实验
1、client -> springboot rest service
2、client -> nginx -> springboot rest service
3、client -> nginx(优化配置) -> springboot rest service
client代码:
for(int i=0; i<1001; i++) { try { StringEntity entity = new StringEntity(JSON.toJSONString(map)); String response = Request.Post(url_nginx).connectTimeout(5000).socketTimeout(5000).addHeader ("Content-Type", "application/json").body(entity).execute().returnContent().asString(); Thread.sleep(1); }catch(Exception e) { } }
nginx配置(优化前):
#user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; server { listen 8089; server_name gateway; location / { proxy_pass http://127.0.0.1:8081; } } }
springboot rest service:就一个非常普通的RestController
@RestController @RequestMapping(value = "system", method = RequestMethod.POST)public class SystemController { private static final Logger LOGGER = LoggerFactory.getLogger(SystemController.class);
@RequestMapping("testGateway") public Response testGateway(@RequestBody ClientRequest clientRequest, HttpServletRequest request ) { long start = System.nanoTime(); Response rd = new Response(); ErrorCode result = ErrorCode.OK; rd.setErrorCode(result.value); rd.setValue(result.memo); long time = System.nanoTime() - start; //LOGGER.info("time:" + time); return rd; } }
结论: 通过client直接调用springboot或者是client调用nginx(配置优化)转发调用springboot,由于每段调用都采用了http1.1连接复用与池化技术,产生的time_wait非常少,占用TCP连接资源非常小。
但是,如果是client调用nginx(未配置优化)转发调用springboot,由于client调用nginx是http1.1而nginx转发调用springboot这时候用的是http1.0,则造成后一段调用每次都会打开-关闭一个tcp连接,生成大量time_wait状态的TCP连接,造成瞬时资源紧张,系统并发能力堪忧。
一般的,TIME_WAIT状态的TCP连接会默认保持4分钟(笔者的windows笔记本由于修改过注册表,tcp只保持30秒,但对于生产服务器来说,优先不应该去改这个参数),我们假设服务器可打开的TCP最大为5000,通过计算也就是说最大并发仅为20 QPS!!!因为240秒之内就已经产生了20*240=4800个time_wait状态的TCP连接了!而就算是笔者公司的云服务器,最大可打开65535个TCP,那么最大并发也不过才273 QPS而已,对于一个互联网应用来说根本不够看!
最后附上优化后的nginx配置:
upstream gateway { server 127.0.0.1:8081; keepalive 10; keepalive_requests 30000; keepalive_timeout 30s; #nginx version 1.15.3以上才有 } server { listen 8088; server_name localhost; location / { proxy_pass http://gateway; proxy_http_version 1.1; proxy_set_header Connection ""; } }
2020-02-07
上面的最后两段结论有不当之处:
nginx -> tomcat这样的反向代理架构,nginx的uptream开启长连接之后,由nginx主动关闭连接,而使用http1.0短连接的话是由tomcat主动关闭连接。
长连接情况下,nginx主动关闭,连接会保持60s,然后time_wait在本地几百个随机端口(取决于并发)。
短链接情况下,tomcat主动关闭,连接马上进入time_wait,然后本地8080端口与远程随机端口的time_wait状态的tcp有5000个。
现象是看来time_wait只影响本地端口作为客户端向外发送的情况,也就是作为客户端,对外建立tcp会由于随机端口不够用而失败。
但是上述ng和tomcat都在一台机器上,系统似乎把tomcat主动关闭的连接算作远程服务,8080的time_wait达到5000,而没有现在本地端口对外建立连接的时候进行限制。