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本身是轮询的,当它发现有新的事件之后 ,会创建线程(这里使用了线程池技术)去处理。