Tomcat NIOソースコード分析(一)--Acceptor
ここでは主にTomcatがNIOを使用して起動し、要求処理を行う大まかな流れについて説明します.使用するソースバージョンは7.0.5です.他の処理などの流れについては書きません.他の文章では大まかに書きましたが、6.0バージョンを使用しています.
http://zddava.iteye.com/category/53603 .
TomcatがNIOを使用するように構成されている場合、起動プロセスは実際には過去とあまり差がなく、Connector#startInternal->Protocol(Http 11 NioProtocol)#start()->Endpoint(NioEndPoint)#start()のプロセスでもあります.ここでは主にNioEndPointを見てみましょう.
ここではまずinit()メソッドを見てみましょう.すべて列挙されていません.最も重要なのは、Server SocketChannelを初期化することです.
Tomcatの各EndpointのAcceptorスレッドは、実際には同じ役割を果たしており、訪問のリクエストを最初に処理するために使用され、NioEndpointのAcceptorも例外ではなく、内部ではRunnableから継承する方法を1つだけ定義しています.
方法は分かりやすいですが、リクエスト用のSocketChannelを入手してPollerに渡すことです.ここでpollはUNIXのシステム呼び出し名です.Java開発者はgoogleの下で、私も「UNIXネットワークプログラミング」をかじる準備をしています.雑言はさておき、setSocketOptions()の方法を見てみましょう.
さて、いよいよPollerですが、次編からPollerです.
http://zddava.iteye.com/category/53603 .
TomcatがNIOを使用するように構成されている場合、起動プロセスは実際には過去とあまり差がなく、Connector#startInternal->Protocol(Http 11 NioProtocol)#start()->Endpoint(NioEndPoint)#start()のプロセスでもあります.ここでは主にNioEndPointを見てみましょう.
public void start() throws Exception {
//
if (!initialized) {
init();
}
if (!running) {
running = true;
paused = false;
// ThreadPoolExecutor , JDK ,
if (getExecutor() == null) {
createExecutor();
}
// poll
pollers = new Poller[getPollerThreadCount()];
for (int i = 0; i < pollers.length; i++) {
pollers[i] = new Poller();
Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-" + i);
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
}
// Acceptor
for (int i = 0; i < acceptorThreadCount; i++) {
Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
acceptorThread.setPriority(threadPriority);
acceptorThread.setDaemon(getDaemon());
acceptorThread.start();
}
}
}
ここではまずinit()メソッドを見てみましょう.すべて列挙されていません.最も重要なのは、Server SocketChannelを初期化することです.
public void init() throws Exception {
if (initialized)
return;
// ServerSocketChannel, , Selector
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());
......
}
Tomcatの各EndpointのAcceptorスレッドは、実際には同じ役割を果たしており、訪問のリクエストを最初に処理するために使用され、NioEndpointのAcceptorも例外ではなく、内部ではRunnableから継承する方法を1つだけ定義しています.
public void run() {
while (running) {
while (paused && running) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore
}
}
if (!running) {
break;
}
try {
//
SocketChannel socket = serverSock.accept();
if ( running && (!paused) && socket != null ) {
// SocketChannel pollor
if (!setSocketOptions(socket)) {
try {
socket.socket().close();
socket.close();
} catch (IOException ix) {
if (log.isDebugEnabled())
log.debug("", ix);
}
}
}
} catch (SocketTimeoutException sx) {
//normal condition
} catch (IOException x) {
if (running) {
log.error(sm.getString("endpoint.accept.fail"), x);
}
} catch (OutOfMemoryError oom) {
try {
oomParachuteData = null;
releaseCaches();
log.error("", oom);
}catch ( Throwable oomt ) {
try {
try {
System.err.println(oomParachuteMsg);
oomt.printStackTrace();
}catch (Throwable letsHopeWeDontGetHere){
ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
}
}catch (Throwable letsHopeWeDontGetHere){
ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("endpoint.accept.fail"), t);
}
}
}
}
方法は分かりやすいですが、リクエスト用のSocketChannelを入手してPollerに渡すことです.ここでpollはUNIXのシステム呼び出し名です.Java開発者はgoogleの下で、私も「UNIXネットワークプログラミング」をかじる準備をしています.雑言はさておき、setSocketOptions()の方法を見てみましょう.
protected boolean setSocketOptions(SocketChannel socket) {
// Process the connection
try {
// disable blocking, APR style, we are gonna be polling it
// NIO
socket.configureBlocking(false);
Socket sock = socket.socket();
socketProperties.setProperties(sock);
// NioChannel ByteChannel
// Channel, NioChannel GC
// SocketChannel
NioChannel channel = nioChannels.poll();
if (channel == null) {
// ?
// NioBufferHandler
// SSL setup
if (sslContext != null) {
SSLEngine engine = createSSLEngine();
int appbufsize = engine.getSession().getApplicationBufferSize();
NioBufferHandler bufhandler = new NioBufferHandler(Math.max(appbufsize,
socketProperties.getAppReadBufSize()), Math.max(appbufsize,
socketProperties.getAppWriteBufSize()), socketProperties.getDirectBuffer());
channel = new SecureNioChannel(socket, engine, bufhandler, selectorPool);
} else {
// normal tcp setup
NioBufferHandler bufhandler = new NioBufferHandler(socketProperties.getAppReadBufSize(),
socketProperties.getAppWriteBufSize(), socketProperties.getDirectBuffer());
channel = new NioChannel(socket, bufhandler);
}
} else {
// Channel
channel.setIOChannel(socket);
if (channel instanceof SecureNioChannel) {
SSLEngine engine = createSSLEngine();
((SecureNioChannel) channel).reset(engine);
} else {
channel.reset();
}
}
// SocketChannel Poller 。
// getPoller0 Poller, Poller 1, 2, 3... n 1, 2, 3....
getPoller0().register(channel);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
try {
log.error("", t);
} catch (Throwable tt) {
ExceptionUtils.handleThrowable(t);
}
// Tell to close the socket
return false;
}
return true;
}
さて、いよいよPollerですが、次編からPollerです.