Tomcat高效响应的秘密(二) keep alive

众所周知,HTTP是TCP/IP的上层协议。而基于TCP/IP的协议,都会涉及到三次握手建立连接,四次挥手结束连接。当然,在请求数量不多的情况下,这些还勉强可以接受。但在互联网上,各种HTTP的请求,动辄成百上千的连接请求,如果每个都握手、挥手,基本都干这些了,正常的数据传输反而不能及时送达了。

为此,在HTTP1.1的时候,将keep alive设置为默认的,以提高HTTP的效率。下图是维基百科上关于keep alive的一个配图,对比打开和关闭keep alive情况下的传输效果。

keep alive,又称为persistent connection,或Http connection reuse。主要作用是用一个TCP连接来处理多个HTTP的请求和响应。好处自然是相当明显的,节省了创建和结束多个连接的时间,可以更专注于数据的传输,页面可以更快的渲染,同时也降低了服务器的负载。

以下几张图来自图解HTTP一书,也很好的说明keep alive的原理

单个请求的情况


多个请求的情况

使用keep alive后的情况

基本原理就是上面这些,而我们在发送一个HTTP请求时,HTTP1.1的请求头中会自动包含connection: keep alive这样的内容。

那是不是keep alive一直开启着就一直有优势呢? 

事情都是有两面性的,如果一直开启,那在大量请求到来时,势必会创建大量的连接,而这些连接一直keep alive,而请求量下降的时候,服务器上会存在不少不工作的连接,依然会影响性能。

下面我们来看,在Tomcat中,对于keep alive是怎么实现的。

前面的文章里写过关于Tomcat的几类Connector,可以看这里

Tomcat的Connector组件

几类Connector包含一个抽象基类AbstractHttp11Processor

在该类中会进行keep alive逻辑的判断处理。

上面内容说keep alive默认是开启的,当然也是可以显式的关闭的,只要在请求头中设置connection:close就可以了。

代码中先是判断请求头中是否包含关闭的配置

MimeHeaders headers = request.getMimeHeaders();

        // Check connection header

        MessageBytes connectionValueMB = headers.getValue(Constants.CONNECTION);

        if (connectionValueMB != null) {

            ByteChunk connectionValueBC = connectionValueMB.getByteChunk();

            if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {// 判断是否有显式的关闭keep-alive

                keepAlive = false;

            } else if (findBytes(connectionValueBC,

                                 Constants.KEEPALIVE_BYTES) != -1) {

                keepAlive = true;

            }

        }

同时,在Connector中还可以配置,允许keep alive的最大连接数

   if (maxKeepAliveRequests == 1) {

                keepAlive = false;

            } else if (maxKeepAliveRequests > 0 &&

                    socketWrapper.decrementKeepAlive() <= 0) {  // 判断允许keep alive的请求数还有多少

                keepAlive = false;

            }

同时在特定条件下,已经开启的keep alive 连接也是要关闭的

// Connection: close header.

        keepAlive = keepAlive && !statusDropsConnection(statusCode); // 根据status code判断是否要把当前连接丢掉

        if (!keepAlive) {

            // Avoid adding the close header twice

            if (!connectionClosePresent) {

            headers.addValue(Constants.CONNECTION).setString(  // 设置响应头中的connection为close

                        Constants.CLOSE);

            }

        } else if (!http11 && !getErrorState().isError()) {

            headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);

        }

对应的以下几类响应状态,keep alive会关闭

/**

     * Determine if we must drop the connection because of the HTTP status

     * code.  Use the same list of codes as Apache/httpd.

     */

    protected boolean statusDropsConnection(int status) {

        return status == 400 /* SC_BAD_REQUEST */ ||

               status == 408 /* SC_REQUEST_TIMEOUT */ ||

               status == 411 /* SC_LENGTH_REQUIRED */ ||

               status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||

               status == 414 /* SC_REQUEST_URI_TOO_LONG */ ||

               status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||

               status == 503 /* SC_SERVICE_UNAVAILABLE */ ||

               status == 501 /* SC_NOT_IMPLEMENTED */;

    }

在服务端传回响应头中包含close,则该连接会关闭。


而实质上,每次的keep alive,是在一个循环里跑,直到到达配置的超时时间后,退出循环。退出时的根据最终Socket的状态,判断是否要保持该keep alive的连接。如果要保持,则会重新添加到线程池中继续执行,例如NIO的处理时,在循环跳出后,finally中最终会执行以下代码,将SocketProcessor添加到cache中。

if (running && !paused) {

                    processorCache.push(this); // 重点是这里。

           }

而Poller在处理特定的事件时,会直接从cache中pop出来

  SocketProcessor sc = processorCache.pop(); // 看这里

            if ( sc == null ) sc = new SocketProcessor(attachment, status);

            else sc.reset(attachment, status);

            Executor executor = getExecutor();

            if (dispatch && executor != null) {

                executor.execute(sc);

            } else {

                sc.run();

          }

BIO中,则是直接将SocketProcessor传到线程池的Executor中。

 getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN_READ));

相关阅读

Tomcat高效响应的秘密(一) Sendfile与Gzip

线程池的原理

和Tomcat学设计模式 | Facade模式与请求处理

Tomcat的Connector组件

Tomcat那些事儿

本公众号由曾从事应用服务器核心研发的工程师维护。文章深入Tomcat源码,分析应用服务器的实现细节,工作原理及与之相关的技术,使用技巧,工作实战等。起于Tomcat但不止于此。同时会分享并发、JVM等,内容多为原创,欢迎关注。


扫描或长按下方二维码,即可关注!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值