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を見てみましょう.
	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です.