文档章节

tomcat的acceptCount与maxConnections

go4it
 go4it
发布于 2017/01/09 23:44
字数 1950
阅读 69
收藏 0

##序 关于tomcat的参数,有acceptCount、maxConnections、maxThreads、minSpareThreads这几个参数比较容易混淆,这里做一下澄清。 ##不同层次

  • maxThreads、minSpareThreads是tomcat工作线程池的配置参数,maxThreads就相当于jdk线程池的maxPoolSize,而minSpareThreads就相当于jdk线程池的corePoolSize。
  • acceptCount、maxConnections是tcp层相关的参数。

输入图片说明

tomcat有一个acceptor线程来accept socket连接,然后有工作线程来进行业务处理。对于client端的一个请求进来,流程是这样的:tcp的三次握手建立连接,建立连接的过程中,OS维护了半连接队列(syn队列)以及完全连接队列(accept队列),在第三次握手之后,server收到了client的ack,则进入establish的状态,然后该连接由syn队列移动到accept队列。tomcat的acceptor线程则负责从accept队列中取出该connection,接受该connection,然后交给工作线程去处理(读取请求参数、处理逻辑、返回响应等等;如果该连接不是keep alived的话,则关闭该连接,然后该工作线程释放回线程池,如果是keep alived的话,则等待下一个数据包的到来直到keepAliveTimeout,然后关闭该连接释放回线程池),然后自己接着去accept队列取connection(当当前socket连接超过maxConnections的时候,acceptor线程自己会阻塞等待,等连接降下去之后,才去处理accept队列的下一个连接)。acceptCount指的就是这个accept队列的大小。

##maxConnections(NioEndpoint$Acceptor) 这个值表示最多可以有多少个socket连接到tomcat上。NIO模式下默认是10000.

// --------------------------------------------------- Acceptor Inner Class
    /**
     * The background thread that listens for incoming TCP/IP connections and
     * hands them off to an appropriate processor.
     */
    protected class Acceptor extends AbstractEndpoint.Acceptor {

        @Override
        public void run() {

            int errorDelay = 0;

            // Loop until we receive a shutdown command
            while (running) {

                // Loop if endpoint is paused
                while (paused && running) {
                    state = AcceptorState.PAUSED;
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }

                if (!running) {
                    break;
                }
                state = AcceptorState.RUNNING;

                try {
                    //if we have reached max connections, wait
                    countUpOrAwaitConnection();

                    SocketChannel socket = null;
                    try {
                        // Accept the next incoming connection from the server
                        // socket
                        socket = serverSock.accept();
                    } catch (IOException ioe) {
                        //we didn't get a socket
                        countDownConnection();
                        // Introduce delay if necessary
                        errorDelay = handleExceptionWithDelay(errorDelay);
                        // re-throw
                        throw ioe;
                    }
                    // Successful accept, reset the error delay
                    errorDelay = 0;

                    // setSocketOptions() will add channel to the poller
                    // if successful
                    if (running && !paused) {
                        if (!setSocketOptions(socket)) {
                            countDownConnection();
                            closeSocket(socket);
                        }
                    } else {
                        countDownConnection();
                        closeSocket(socket);
                    }
                } catch (SocketTimeoutException sx) {
                    // Ignore: Normal condition
                } catch (IOException x) {
                    if (running) {
                        log.error(sm.getString("endpoint.accept.fail"), x);
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("endpoint.accept.fail"), t);
                }
            }
            state = AcceptorState.ENDED;
        }
    }

这里countUpOrAwaitConnection()判断的就是当前的连接数是否超过maxConnections。

##acceptCount(backlog) 在源码里头是backlog参数,默认值为100。该参数是指当前连接数超过maxConnections的时候,还可接受的连接数,即tcp的完全连接队列(accept队列)的大小。

backlog参数提示内核监听队列的最大长度。监听队列的长度如果超过backlog,服务器将不受理新的客户连接,客户端也将收到ECONNREFUSED错误信息。在内核版本2.2之前的Linux中,backlog参数是指所有处于半连接状态(SYN_RCVD)和完全连接状态(ESTABLISHED)的socket的上限。但自内核版本2.2之后,它只表示处于完全连接状态的socket的上限,处于半连接状态的socket的上限则由/proc/sys/net/ipv4/tcp_max_syn_backlog内核参数定义。

###TCP三次握手队列

  • client端的socket等待队列: 当第一次握手,建立半连接状态:client 通过 connect 向 server 发出 SYN 包时,client 会维护一个 socket 队列,如果 socket 等待队列满了,而 client 也会由此返回 connection time out,只要是 client 没有收到 第二次握手SYN+ACK,3s 之后,client 会再次发送,如果依然没有收到,9s 之后会继续发送。

  • server端的半连接队列(syn队列): 此时server 会维护一个 SYN 队列,半连接 syn 队列的长度为 max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog)  ,在机器的tcp_max_syn_backlog值在/proc/sys/net/ipv4/tcp_max_syn_backlog下配置,当 server 收到 client 的 SYN 包后,会进行第二次握手发送SYN+ACK 的包加以确认,client 的 TCP 协议栈会唤醒 socket 等待队列,发出 connect 调用。

  • server端的完全连接队列(accpet队列): 当第三次握手时,当server接收到ACK 报之后, 会进入一个新的叫 accept 的队列,该队列的长度为 min(backlog, somaxconn),默认情况下,somaxconn 的值为 128,表示最多有 129 的 ESTAB 的连接等待 accept(),而 backlog 的值则应该是由 int listen(int sockfd, int backlog) 中的第二个参数指定,listen 里面的 backlog 可以有我们的应用程序去定义的。

NioEndpoint的bind方法

@Override
    public void bind() throws Exception {

        serverSock = ServerSocketChannel.open();
        socketProperties.setProperties(serverSock.socket());
        InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
        serverSock.socket().bind(addr,getBacklog());
        serverSock.configureBlocking(true); //mimic APR behavior
        serverSock.socket().setSoTimeout(getSocketProperties().getSoTimeout());

        // Initialize thread count defaults for acceptor, poller
        if (acceptorThreadCount == 0) {
            // FIXME: Doesn't seem to work that well with multiple accept threads
            acceptorThreadCount = 1;
        }
        if (pollerThreadCount <= 0) {
            //minimum one poller thread
            pollerThreadCount = 1;
        }
        stopLatch = new CountDownLatch(pollerThreadCount);

        // Initialize SSL if needed
        initialiseSsl();

        selectorPool.open();
    }

这里的serverSock.socket().bind(addr,getBacklog());的backlog就是acceptCount参数值。

###tcp的半连接与完全连接队列 当accept队列满了之后,即使client继续向server发送ACK的包,也会不被相应,此时,server通过/proc/sys/net/ipv4/tcp_abort_on_overflow来决定如何返回,0表示直接丢丢弃该ACK,1表示发送RST通知client;相应的,client则会分别返回read timeout 或者 connection reset by peer。

输入图片说明

总的来说:可以看到,整个TCP连接中我们的Server端有如下的两个 queue:

  • 一个是半连接队列:(syn queue) queue(max(tcp_max_syn_backlog, 64)),用来保存 SYN_SENT 以及 SYN_RECV 的信息。
  • 另外一个是完全连接队列:accept queue(min(somaxconn, backlog)),保存 ESTAB 的状态,那么建立连接之后,我们的应用服务的线程就可以accept()处理业务需求了。

###SYN攻击 在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是 Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址 是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:

netstat -nap | grep SYN_RECV

##关于maxConnections与maxThreads maxConnections表示有多少个socket连接到tomcat上。NIO模式下默认是10000。而maxThreads则是woker线程并发处理请求的最大数。也就是虽然client的socket连接上了,但是可能都在tomcat的task queue里头,等待worker线程处理返回响应。

##小结 tomcat server在tcp的accept队列的大小设置的基础上,对请求连接多做了一层保护,也就是maxConnections的大小限制。

当client端的大量请求过来时,首先是OS层的tcp的accept队列帮忙挡住,accept队列满了的话,后续的连接无法进入accept队列,无法交由工作线程处理,client将得到read timeout或者connection reset的错误。

第二层保护就是,在acceptor线程里头进行缓冲,当连接的socket超过maxConnections的时候,则进行阻塞等待,控制acceptor转给worker线程连接的速度,稍微缓缓,等待worker线程处理响应client。

##doc

© 著作权归作者所有

go4it
粉丝 88
博文 1157
码字总数 1088677
作品 0
深圳
私信 提问
总结:nginx502:Tomcat调优之acceptCount

问题背景:UI页面点击会偶尔返回error,检查调用日志,发现nginx报502报错,因此本文即排查502报错原因。 如下红框可知,访问本机个备机的服务502了,用时3秒左右(可见并不是超时) 先给出原...

浮躁的码农
04/23
28
0
聊下并发和Tomcat线程数

最近一直在解决线上一个问题,表现是:Tomcat每到凌晨会有一个高峰,峰值的并发达到了3000以上,最后的结果是Tomcat线程池满了,日志看很多请求超过了1s。服务器性能很好,Tomcat版本是7.0.5...

jEpac
2016/07/26
87
0
详解:tomcat的连接数与线程池

转自:http://www.cnblogs.com/kismetv/p/7806063.html前言在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector)。 在前面...

goldfishe
2017/12/22
0
0
tomcat中的server.xml

tomcathomedirectory/conf/server.xml中: 配置: <!-- 自定义一个线程池:name --> <!-- 属性: --> <!-- name:线程池的名称 --> <!-- namePrefix:线程池中线程名的前缀,默认为catalina-......

A__17
2018/07/02
3
0
详解 Tomcat 的连接数与线程池

前言 在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector)。 在前面的文章 详解Tomcat配置文件server.xml 中写到过:Con...

t4i2b10X4c22nF6A
2017/11/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

如何保证消息不被重复消费

如何保证消息不被重复消费?(如何保证消息消费的幂等性) 举个例子吧。假设你有个系统,消费一条消息就往数据库里插入一条数据,要是你一个消息重复两次,你不就插入了两条,这数据不就错了...

五彩的颜色
6分钟前
1
0
python3.7 django2.2 mysql 异常

错误日志 mysqlclient 1.3.13 or newer is required; File "/Users/huoyinghui/workspaces/drf3/lib/python3.7/site-packages/django/db/utils.py", line 201, in __getitem__ backend =......

hyhlinux
8分钟前
4
0
【2019年8月版】OCP 071认证考试原题-第34题

Choose two. Which two statements are true about the results of using the INTERSECT operator in compound queres? A) Reversing the order of the intersected tables can sometimes af......

oschina_5359
11分钟前
2
0
Dev 日志 | 一次 Segmentation Fault 和 GCC Illegal Instruction 编译问题排查

摘要 笔者最近在重新整理和编译 Nebula Graph 的第三方依赖,选出两个比较有意思的问题给大家分享一下。 Flex Segmentation Fault——Segmentation fault (core dumped) 在编译 Flex 过程中,...

NebulaGraph
11分钟前
4
0
在Windows中安装OpenCV-Python |四

目标 在本教程中 我们将学习在你的Windows系统中设置OpenCV-Python。 下面的步骤在装有Visual Studio 2010和Visual Studio 2012的Windows 7-64位计算机上进行了测试。屏幕截图展示的是VS201...

人工智能遇见磐创
12分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部