tomcat的Endpoint

maxConnections设置最大线程数,默认是10000(tomcat 8.5,默认使用NIO的情况下。如果低于这个版本并且使用BIO,则可能只有200),这个参数可以设置。

该参数作用于connectionLimitLatch,每次设置maxConnections的时候都会附带着修改connectionLimitLatch,如果maxConnections是-1,则释放所有的锁,然后将connectionLimitLatch置为null。

 this.connectionLimitLatch = new LimitLatch((long)this.getMaxConnections())

public void setMaxConnections(int maxCon) {
        this.maxConnections = maxCon;
        LimitLatch latch = this.connectionLimitLatch;
        if(latch != null) {
            if(maxCon == -1) {
                this.releaseConnectionLatch();
            } else {
                latch.setLimit((long)maxCon);
            }
        } else if(maxCon > 0) {
            this.initializeConnectionLatch();
        }

    }

connectionLimitLatch就是一个计数器,每增加一个conn,计数器就增加1,达到limit时,线程会阻塞。

  protected void countUpOrAwaitConnection() throws InterruptedException {
        if(this.maxConnections != -1) {
            LimitLatch latch = this.connectionLimitLatch;
            if(latch != null) {
                latch.countUpOrAwait();
            }

        }
    }

acceptor是Endpoint的重要组件,用于接收套接字。
使用布尔值running,以便及时终止线程。每轮循环都会判断应用有没有被暂停或者终止,如果有就休眠一段时间。这两个变量都是volatile的,以便不同线程的可见性。

然后在 serverSock处阻塞,直到有一个套接字被创建之后。

public void run() {
            byte errorDelay = 0;

            while(NioEndpoint.this.running) {
                while(NioEndpoint.this.paused && NioEndpoint.this.running) {
                    this.state = AcceptorState.PAUSED;

                    try {
                        Thread.sleep(50L);
                    } catch (InterruptedException var5) {
                        ;
                    }
                }

                if(!NioEndpoint.this.running) {
                    break;
                }

                this.state = AcceptorState.RUNNING;

                try {
                    NioEndpoint.this.countUpOrAwaitConnection();  //增加一个计数
/*

protected int tryAcquireShared(int ignored) {
            long newCount = LimitLatch.this.count.incrementAndGet();
            if(!LimitLatch.this.released && newCount > LimitLatch.this.limit) {
                LimitLatch.this.count.decrementAndGet();
                return -1;
            } else {
                return 1;
            }
        }

*/

                    SocketChannel socket = null;

                    try {
                        socket = NioEndpoint.this.serverSock.accept();
                    } catch (IOException var4) {
                        NioEndpoint.this.countDownConnection();
                        NioEndpoint.this.handleExceptionWithDelay(errorDelay);
                        throw var4;
                    }

                    errorDelay = 0;
                    if(NioEndpoint.this.running && !NioEndpoint.this.paused) {
                        if(!NioEndpoint.this.setSocketOptions(socket)) {
                            NioEndpoint.this.countDownConnection();
                            NioEndpoint.this.closeSocket(socket);
                        }
                    } else {
                        NioEndpoint.this.countDownConnection();
                        NioEndpoint.this.closeSocket(socket);
                    }
                } catch (SocketTimeoutException var6) {
                } catch (IOException var7) {     
                } catch (Throwable var8) {
                    ExceptionUtils.handleThrowable(var8);         
            }

            this.state = AcceptorState.ENDED;
        }

拥有一个套接字之后,配置为非阻塞的,设置一些属性。

从通道池nioChannels中取出一个通道,把socket注册到通道nioChannel。

每accept获取到一个socketChannel(都是新创建的),socketChannel和socket也是一一对应的.

总的来说,获取到从浏览器发起的请求后,会创建套接字(由acceptor,这是一个单线程的),然后设置一些值之后,把套接字放到一个轮询线程的events(初识容量128,默认最大容量500)队列中。

轮询池pollers,数量一般是2,属于守护线程,在被创建的时候启动。它有一个同步队列events(SynchronizedQueue),它会不断检查队列中是否有事件。

 protected boolean setSocketOptions(SocketChannel socket) {
        try {
            socket.configureBlocking(false);
            Socket sock = socket.socket();
            this.socketProperties.setProperties(sock);
            NioChannel channel = (NioChannel)this.nioChannels.pop();
            if(channel == null) {
            //创建一个channel = new NioChannel(socket, bufhandler);
            //bufhandler 主要是记录读、写以及直接内存的缓存大小,单位是字节。
                SocketBufferHandler bufhandler = new SocketBufferHandler(this.socketProperties.getAppReadBufSize(), this.socketProperties.getAppWriteBufSize(), this.socketProperties.getDirectBuffer());
                if(this.isSSLEnabled()) {
                    channel = new SecureNioChannel(socket, bufhandler, this.selectorPool, this);
                } else {
                    channel = new NioChannel(socket, bufhandler);
                }
            } else {
                ((NioChannel)channel).setIOChannel(socket);
                //每个通道同时只能有一个socketChannel
                ((NioChannel)channel).reset();
            }
//pollers最多只有2个,可能只有一个(但是现在都是多核处理器,所以基本上没可能)。
//这里通过轮询的方式获取一个poller,把通道注册到poller上
            this.getPoller0().register((NioChannel)channel);
            return true;
        } catch (Throwable var6) {
            Throwable t = var6;
            ExceptionUtils.handleThrowable(var6);

            try {
                log.error("", t);
            } catch (Throwable var5) {
                ExceptionUtils.handleThrowable(var5);
            }

            return false;
        }
    }
//selectNow,获取已经可以进行I/O操作的通道的keys,如果没有则直接返回0
//select,会阻塞到有可以用的为止

PollerEvent的run方法,
轮询线程在发现有新的事件加入的时候(这里就是有新的网络套接字创建),都会为事件创建一个线程去处理。

新加入的pollerEvent事件,interestOps都是256.

public void run() {
            if(this.interestOps == 256) {
                try {
                    this.socket.getIOChannel().register(this.socket.getPoller().getSelector(), 1, this.socketWrapper);
                } catch (Exception var6) {
                    NioEndpoint.log.error(AbstractEndpoint.sm.getString("endpoint.nio.registerFail"), var6);
                }
            } else {
                SelectionKey key = this.socket.getIOChannel().keyFor(this.socket.getPoller().getSelector());

                try {
                    if(key == null) {
                        this.socket.socketWrapper.getEndpoint().countDownConnection();
                        ((NioEndpoint.NioSocketWrapper)this.socket.socketWrapper).closed = true;
                    } else {
                        NioEndpoint.NioSocketWrapper socketWrapper = (NioEndpoint.NioSocketWrapper)key.attachment();
                        if(socketWrapper != null) {
                            int ops = key.interestOps() | this.interestOps;
                            socketWrapper.interestOps(ops);
                            key.interestOps(ops);
                        } else {
                            this.socket.getPoller().cancelledKey(key);
                        }
                    }
                } catch (CancelledKeyException var5) {
                    try {
                        this.socket.getPoller().cancelledKey(key);
                    } catch (Exception var4) {
                        ;
                    }
                }
            }

        }

这里socketChannel的包装类只对读事件感兴趣,因为这是接收到用户请求时触发的(那么肯定是先读取浏览器的请求,而不是直接给对方发消息,这方面服务器一开始肯定是被动的)。

总的来说,就是acceptor(一般是单线程的)等待套接字连接,有了就把含有套接字的事件添加到poller池(一般有2个poller,每个Poller都是一个线程)中的任意一个poller中,Poller本身是轮询的,当它发现有新的事件之后 ,会创建线程(这里使用了线程池技术)去处理。

猜你喜欢

转载自blog.csdn.net/ljz2016/article/details/84938502
今日推荐