Web服务HTTP接口开启keep alive

keep alive的作用网上资料很多了,需不需要开启,开启时候设置多长,都是要根据项目实际情况来设置的,就算开启了,是不是真的做到了从客户端请求经过层层代理到后端服务器,都是一条线的keep alive维系的,这是一个心细活,需要在上下游的各个节点协作配置才能打通所有环节。

项目架构

目前的项目的结构如下图所示:
https://static.yvonxiao.com/blog/keepalive/architecture_1.jpg

外网的交互采用https/http2协议,内网的交互采用http1.1协议,虽然这样会比一些二进制协议性能差一些,但是方便提高整体项目的可维护性,可水平扩展性和开发便捷性,其实差也差不了多少,系统的瓶颈不在这里,更何况这个项目仅仅是一个跨终端的SaaS项目,对信息交互没有到那种极致的要求,而且开启了keep alive甚至http2后,连接性能方面更是可以不用担心了。

后端相关配置

api服务器是负责主要业务的,然后分别向内网和移动端提供rest api接口,不同的连接策略提供一致的业务数据。

这里先看看后端服务器的情况,在内网情况下,肯定希望保持和内网机器的有效长连接来减少频繁新建连接带来的消耗成本,提高性能,外网连接主要是移动端,考虑到后面移动端设备数目可能会很多的情况下,就算开启keep-alive,keepalive_timeout也要设置一个合适的值,不能让空闲长连接过多的占用端口和资源。

因为项目还没有正式上线,所以不能根据实际情况来动态的调整参数,一般来说,有2个参数是需要注意的,一个是keepalive_timeout,另一个是最大并发空闲keepalive数。比如内网环境,如果是有限的几台机器来请求资源,自然是保持较长时间的长连接会好一些,这个时候就要设置keepalive_timeout为一个较长的值。又比如服务器是10000的QPS,响应时间是100ms,那么显然长连接的数目是1000,设置keepalive数的时候就要根据服务器状况设置这个长连接数目的10%到30%。当然具体环境肯定不是这么理想,肯定是要设置一个在可接受范围内的值。

这里仅仅是演示正确的连接方式,所以设置一个60s作为keepalive_timeout的值,这也是参考了所有浏览器默认的timeout,取了一个最小的。目前为止项目的并发不是很多,所以取了100作为keepalive数(当这个数超过时,会采用LUR算法来淘汰并关闭连接)

很多人配置长连接,仅仅是nginx上随便设置一下就没了,实际上是需要和后端服务器一起配置的,不然一个长连接,一个短连接,反而会引起服务器频繁的主动关闭请求造成大量的TIME_WAIT。

nginx(nginx.conf)

http {
    #根据keepalive_timeout时间内服务器的keep alive请求总数来设置
    keepalive_requests 1620;
    #第二个参数可以设置Keep-Alive: timeout=time响应头的值
    keepalive_timeout 60s 60s;

    upstream remote_api {
        server api服务器ip:8080 weight=1 max_fails=3 fail_tmeout=30s;
        # 这个需要根据实际情况来调整
        keepalive 100;
    }
    
    server {
        ...
        location /api {
            proxy_pass http://remote_api/api;

            #设置http协议版本
            proxy_http_version 1.1;
            #允许重新定义或追加字段到传递给代理服务器的请求头信息(默认是close)
            proxy_set_header Connection "";

    	    proxy_redirect off;
    	    proxy_set_header Host $host;
    	    proxy_set_header X-Real-IP $remote_addr;
    	    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    	    proxy_set_header X-Forwarded-Proto $scheme;
    	    error_page 404 500 @proxy;

        }
    }
    ...
}

Tomcat(conf/server.xml)
这里只贴出和keep alive相关的,其他的配置根据具体情况来修改

    <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
	       maxThreads="512"
	       minSpareThreads="10"
	       acceptCount="768"
               connectionTimeout="1000"
	       maxConnections="1280"

	       keepAliveTimeout="60000"
	       maxKeepAliveRequests="100"

               redirectPort="8443"  URIEncoding="UTF-8"  enableLookups="false" />

上面的keepAliveTimeoutmaxKeepAliveRequests分别和nginx配置里的keepalive_timeoutkeepalive保持一致

前端相关配置

前端系统里的nginx和后端系统里的nginx配置是一样的,只是代理到了node,其实在小系统里,node直接连后端的tomcat也是可以的,这样也是为了彻底前后端分离,视图渲染和页面逻辑彻底给node,而业务逻辑给java,通常默认情况下(java开发中不使用Netty等nio框架,仅仅是默认的Tomcat),一台node可以带起后面的4到6台Java。

回到这里,为了更干净的让前端node不需要关心后端服务器的负载均衡,还是在node和Tomcat之间用nginx充当了一层负载均衡,这里使用nginx是因为目前服务就几台阿里云的服务器,不需要采用大型项目里流行的ha配置虚ip的方式,直接用nginx简单处理一下即可,况且后端还是要直接暴露rest api给外网的移动端用,使用nginx处理也简单得多。

node要注意的
在发送http请求的时候,注意配置下http.Agent

var keepAliveAgent = new http.Agent({ 
keepAlive: true,
// 注意和上下游保持一致
keepAliveMsecs: 60000,
// 这个要根据实际情况来配置
maxSockets: 100
});
前端http2升级

这个可以参考之前的CentOS 6.7 (64位)配置nginx支持http2,可以改善外网连接性能,内网的话没有那么多同时请求动静态资源的场景,都是简单的类似RMI一样的rest api连接,不需要在http应用层之上的多路复用,所以只要保持长连接就足够了。

https://static.yvonxiao.com/blog/keepalive/netstat_ESTABLISHED.png

上图中,前4个连接是我以http协议打开外网网站产生的,虽然也是keep alive,但是只是重用了tcp的连接,页面的其他并发请求仍然要新开连接。最后一个是用http2(url访问上是https)访问外网网站,只开启了一个连接,真正做到了http协议上的多路复用。

总结
  1. 对于很多动静态请求都在同一个网站的服务,非常适合http2或者打开http1.1的keep-alive
  2. 如果是单一的对外接口服务,类似RMI那种调用一下请求获取结果就关闭的场景,不适合打开keep-alive
  3. 上述场景如果是在内网里,发送请求方数目有限,想获取比较快的响应速度,那么维持内网内一个长连接是比较好的选择,可以打开keep-alive
  4. 设置参数的时候一定要根据实际情况来调整

参考

Show Comments