NIOEndPoint
NIOEndPoint
的bind()
方法開啟一個SocketServer
@Override
public void bind() throws Exception {
//開啟一個server socket
serverSock = ServerSocketChannel.open();
//根據(jù)配置文件設置server socket的屬性
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
serverSock.socket().bind(addr,getAcceptCount());
serverSock.configureBlocking(true); //mimic APR behavior
// 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);
// 如果需要的話,初始化SSL
initialiseSsl();
//開啟Selector,堅挺NIO的 IO的事件
selectorPool.open();
}
·
Acceptor線程接收客戶請求
在Tomcat啟動的時候會啟動一個Endpoint,并會調(diào)用它的startInternal方法,在這里開啟了一個Acceptor的子線程。利用這個Acceptor子線程來接收Client端的Socket連接
@Override
public void run() {
........................省略代碼................................................
// 這里調(diào)用上面NIOEndPoint 的serverSocketChannel 來接收一個客戶端發(fā)送來的socket
socket = endpoint.serverSocketAccept();
........................省略代碼................................................
// setSocketOptions() will hand the socket off to
// an appropriate processor if successful
if (!endpoint.setSocketOptions(socket)) {
//及時關閉Socket連接
endpoint.closeSocket(socket);
}
}
從Tomcat8 以后,Tomcat的默認連接為NIO。所以在這里的EndPoint的具體實現(xiàn)是NioEndPoint
@Override
protected boolean setSocketOptions(SocketChannel socket) {
// Process the connection
try {
//disable blocking, APR style, we are gonna be polling it
socket.configureBlocking(false);
Socket sock = socket.socket();
socketProperties.setProperties(sock);
NioChannel channel = nioChannels.pop();
if (channel == null) {
SocketBufferHandler bufhandler = new SocketBufferHandler(
socketProperties.getAppReadBufSize(),
socketProperties.getAppWriteBufSize(),
socketProperties.getDirectBuffer());
if (isSSLEnabled()) {
channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
} else {
// NIOChannel 實質(zhì)上對ByteChannel 的一個封裝實現(xiàn)
channel = new NioChannel(socket, bufhandler);
}
} else {
// 根據(jù)Socket,對設置當前的NioChannel
channel.setIOChannel(socket);
channel.reset();
}
getPoller0().register(channel);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
try {
log.error("",t);
} catch (Throwable tt) {
ExceptionUtils.handleThrowable(tt);
}
// Tell to close the socket
return false;
}
return true;
}
通過閱讀代碼可以看出其處理過程如下:
設置非阻塞,以及其他的一些參數(shù)如SoTimeout、ReceiveBufferSize、SendBufferSize
然后將SocketChannel封裝成一個NioChannel,封裝過程使用了緩存,即避免了重復創(chuàng)建NioChannel,封裝過程中使用了緩存,既避免了重復創(chuàng)建NioChannel對象,直接利用原有的NIOChannel,并將NioChannel中的數(shù)據(jù)全部清空。
選擇一個Poller進行注冊
再來看一下Poller
public void register(final NioChannel socket) {
socket.setPoller(this);
NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
socket.setSocketWrapper(ka);
ka.setPoller(this);
ka.setReadTimeout(getConnectionTimeout());
ka.setWriteTimeout(getConnectionTimeout());
ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
ka.setSecure(isSSLEnabled());
PollerEvent r = eventCache.pop();
ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
else r.reset(socket,ka,OP_REGISTER); //
addEvent(r); //將event 加入到事件處理隊列中
}
這里又是進行一些參數(shù)包裝,將socket和Poller的關系綁定,再次從緩存中取出或者重新構建一個PollerEvent,然后將該event放到Poller的事件隊列中等待被異步處理
再來看看對于被加入到隊列中的events的處理
public boolean events() {
boolean result = false;
PollerEvent pe = null;
while ( (pe = events.poll()) != null ) {
result = true;
try {
pe.run(); //開始處理這個pollerEvent
pe.reset(); //
if (running && !paused) {
eventCache.push(pe);
}
} catch ( Throwable x ) {
log.error("",x);
}
}
return result;
}
Poller的run 方法會對
@Override
public void run() {
.........................省略代碼................................
// 遍歷并處理 已經(jīng)準備好的NIO 事件
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
if (attachment == null) {
iterator.remove();
} else {
iterator.remove();
//將已經(jīng)準備好的IO事件和 綁定的Socket進行分類處理
processKey(sk, attachment);
}
}//while
............................省略代碼.............
}
protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
try {
if ( close ) {
cancelledKey(sk);
} else if ( sk.isValid() && attachment != null ) {
if (sk.isReadable() || sk.isWritable() ) {
if ( attachment.getSendfileData() != null ) {
processSendfile(sk,attachment, false);
} else {
//為了避免多線程對于attach的socketChannel處理的沖突,在這里將socketChannel的
// ready 操作位取反
unreg(sk, attachment, sk.readyOps());
boolean closeSocket = false;
// 如果SelectorKey 為Read,那就去處理Read
if (sk.isReadable()) {
if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
closeSocket = true;
}
}
// 如果SelectorKey 為Read,那就去處理Write
if (!closeSocket && sk.isWritable()) {
if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
closeSocket = true;
}
}
if (closeSocket) {
// 將attach的socketChannel從key中解綁
cancelledKey(sk);
}
}
}
} else {
//invalid key
cancelledKey(sk);
}
} catch ( CancelledKeyException ckx ) {
cancelledKey(sk);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("",t);
}
}
/**
* Process the given SocketWrapper with the given status. Used to trigger
* processing as if the Poller (for those endpoints that have one)
* selected the socket.
*
* @param socketWrapper The socket wrapper to process
* @param event The socket event to be processed
* @param dispatch Should the processing be performed on a new
* container thread
*
* @return if processing was triggered successfully
*/
public boolean processSocket(SocketWrapperBase<S> socketWrapper,
SocketEvent event, boolean dispatch) {
try {
if (socketWrapper == null) {
return false;
}
SocketProcessorBase<S> sc = processorCache.pop();
if (sc == null) {
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
Executor executor = getExecutor();
if (dispatch && executor != null) {
//將SocketProcessorBase交給線程池中的線程來處理
executor.execute(sc);
} else {
sc.run();
}
} catch (RejectedExecutionException ree) {
getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
return false;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This means we got an OOM or similar creating a thread, or that
// the pool and its queue are full
getLog().error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}
線程池中的線程即為 NioEndPonint$SocketProcessor
內(nèi)部類來看一下其內(nèi)部的doRun()
方法
doRun{
.....................
//如果握手已經(jīng)完成....
if (handshake == 0) {
SocketState state = SocketState.OPEN;
// Process the request from this socket
if (event == null) {
state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
} else {
// 這里是關鍵
state = getHandler().process(socketWrapper, event);
}
if (state == SocketState.CLOSED) {
close(socket, key);
}
}
....................
}
從上面代碼中可以看出,注意它調(diào)用了hanler.process(socket)來生成響應數(shù)據(jù)。并且根據(jù)處理之后的返回狀態(tài)來決定是否關閉Socket連接
對于handler.process(socket)的處理。
NioEndpoint類中的Handler接口的具體實現(xiàn)是靜態(tài)類 AbstractProtocol$ConnectionHandler<S>
通過查看其process(socket)
方法.這個方法邏輯很長并且很復雜。說多了也沒用。
它在這個方法里做的動作
- 通過協(xié)議類型得到相應的Socket的 Processor,并cache起來,在這里這個Processor就是
Http11Processor
- 通過一個ThreadLocal 類標識現(xiàn)在的這個線程正在處理一個請求
- 調(diào)用Processor的process方法。
上面的Processor的process方法通過抽象類間接的調(diào)用了Http11Processor
的service()
方法。這個service()
方法也是相當復雜`
它主要完成的動作有
- 填充Request,Reponse屬性
- 調(diào)用
CoyoteAdapter
的service()方法
通過以上不走,一個請求連接就從Connector走到了Container
小結:
實現(xiàn)一個tomcat連接器Connector就是實現(xiàn)ProtocolHander接口的過程。Connector用來接收Socket Client端的請求,通過內(nèi)置的線程池去調(diào)用Servlet Container生成響應結果,并將響應結果同步或異步的返回給Socket Client。在第三方應用集成tomcat作為Web容器時,一般不會動Servlet Container端的代碼,那么connector的性能將是整個Web容器性能的關鍵。