隨筆-9  評論-3  文章-0  trackbacks-0

          這里主要講一下Tomcat使用NIO啟動和進行請求處理的大致流程,使用的源碼版本是7.0.5,對于其他處理等流程就不寫了,我在別的文章里已經大致寫過了,不過是用的6.0版本:http://zddava.javaeye.com/category/53603

          當Tomcat配置成使用NIO時,啟動過程其實和過去差不多,也是Connector#startInternal -> Protocol(Http11NioProtocol)#start() -> Endpoint(NioEndPoint)#start()的過程,這里主要看一下NioEndPoint:

           1    public void start() throws Exception {
           2        // 初始化
           3        if (!initialized) {
           4            init();
           5        }

           6        if (!running) {
           7            running = true;
           8            paused = false;
           9
          10            // 創建一個ThreadPoolExecutor對象,和JDK里的功能一樣,只不過進行了一些擴展
          11            if (getExecutor() == null{
          12                createExecutor();
          13            }

          14
          15            // 開啟poll的線程
          16            pollers = new Poller[getPollerThreadCount()];
          17            for (int i = 0; i < pollers.length; i++{
          18                pollers[i] = new Poller();
          19                Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-" + i);
          20                pollerThread.setPriority(threadPriority);
          21                pollerThread.setDaemon(true);
          22                pollerThread.start();
          23            }

          24
          25            // 開啟Acceptor的線程
          26            for (int i = 0; i < acceptorThreadCount; i++{
          27                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
          28                acceptorThread.setPriority(threadPriority);
          29                acceptorThread.setDaemon(getDaemon());
          30                acceptorThread.start();
          31            }

          32        }

          33    }

          這里先看一下init()方法,沒有全列出來,最主要的一點就是初始化ServerSocketChannel:

           1    public void init() throws Exception {
           2
           3        if (initialized)
           4            return;
           5
           6        // 初始化ServerSocketChannel,這里用的是阻塞的方式,沒有用Selector
           7        serverSock = ServerSocketChannel.open();
           8        socketProperties.setProperties(serverSock.socket());
           9        InetSocketAddress addr = (getAddress() != null ? new InetSocketAddress(getAddress(), getPort())
          10                : new InetSocketAddress(getPort()));
          11        serverSock.socket().bind(addr, getBacklog());
          12        serverSock.configureBlocking(true); // mimic APR behavior
          13        serverSock.socket().setSoTimeout(getSocketProperties().getSoTimeout());
          14
          15        ......
          16
          17    }

          Tomcat每種Endpoint的Acceptor線程其實作用都一樣,對來訪的請求進行最初的處理之用,NioEndpoint的Acceptor也不例外,它內部也只定義一個繼承自Runnable的方法

           1        public void run() {
           2            while (running) {
           3                
           4                while (paused && running) {
           5                    try {
           6                        Thread.sleep(1000);
           7                    }
           catch (InterruptedException e) {
           8                        // Ignore
           9                    }

          10                }

          11
          12                if (!running) {
          13                    break;
          14                }

          15                try {
          16            // 接受請求
          17                    SocketChannel socket = serverSock.accept();
          18                    if ( running && (!paused) && socket != null ) {
          19                        // 將SocketChannel給pollor處理
          20                        if (!setSocketOptions(socket)) {
          21                            try {
          22                                socket.socket().close();
          23                                socket.close();
          24                            }
           catch (IOException ix) {
          25                                if (log.isDebugEnabled())
          26                                    log.debug("", ix);
          27                            }

          28                        }
           
          29                    }

          30                }
           catch (SocketTimeoutException sx) {
          31                    //normal condition
          32                }
           catch (IOException x) {
          33                    if (running) {
          34                        log.error(sm.getString("endpoint.accept.fail"), x);
          35                    }

          36                }
           catch (OutOfMemoryError oom) {
          37                    try {
          38                        oomParachuteData = null;
          39                        releaseCaches();
          40                        log.error("", oom);
          41                    }
          catch ( Throwable oomt ) {
          42                        try {
          43                            try {
          44                                System.err.println(oomParachuteMsg);
          45                                oomt.printStackTrace();
          46                            }
          catch (Throwable letsHopeWeDontGetHere){
          47                                ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
          48                            }

          49                        }
          catch (Throwable letsHopeWeDontGetHere){
          50                            ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
          51                        }

          52                    }

          53                }
           catch (Throwable t) {
          54                    ExceptionUtils.handleThrowable(t);
          55                    log.error(sm.getString("endpoint.accept.fail"), t);
          56                }

          57            }

          58        }

          59    }

          方法其實挺容易理解,就是得到請求用的SocketChannel后交給Poller處理,這里poll是一個UNIX的系統調用名字,Java開發者可以google下,我也是才準備開始啃《UNIX網絡編程》,閑言少敘,看一下#setSocketOptions()方法吧:

           1    protected boolean setSocketOptions(SocketChannel socket) {
           2        // Process the connection
           3        try {
           4            // disable blocking, APR style, we are gonna be polling it
           5            // 這里終于看到了印象中的NIO的影子了
           6            socket.configureBlocking(false);
           7            Socket sock = socket.socket();
           8            socketProperties.setProperties(sock);
           9
          10            // NioChannel是ByteChannel的子類
          11            // 從隊列里取出第一個可用的Channel,這樣的話NioChannel應該是設計成非GC的
          12            // 感覺其目的主要是對SocketChannel進行下封裝
          13            NioChannel channel = nioChannels.poll();
          14            if (channel == null{
          15                // 不過這里如果沒有可用的就初始化一個的話請求數陡然增高再慢慢回落的時候不就浪費了內存了嗎?
          16                // NioBufferHandler里分別分配了讀緩沖區和寫緩沖區
          17                // SSL setup
          18                if (sslContext != null{
          19                    SSLEngine engine = createSSLEngine();
          20                    int appbufsize = engine.getSession().getApplicationBufferSize();
          21                    NioBufferHandler bufhandler = new NioBufferHandler(Math.max(appbufsize,
          22                            socketProperties.getAppReadBufSize()), Math.max(appbufsize,
          23                            socketProperties.getAppWriteBufSize()), socketProperties.getDirectBuffer());
          24                    channel = new SecureNioChannel(socket, engine, bufhandler, selectorPool);
          25                }
           else {
          26                    // normal tcp setup
          27                    NioBufferHandler bufhandler = new NioBufferHandler(socketProperties.getAppReadBufSize(),
          28                            socketProperties.getAppWriteBufSize(), socketProperties.getDirectBuffer());
          29
          30                    channel = new NioChannel(socket, bufhandler);
          31                }

          32            }
           else {
          33                // 這里就是對Channel的重用了
          34                channel.setIOChannel(socket);
          35                if (channel instanceof SecureNioChannel) {
          36                    SSLEngine engine = createSSLEngine();
          37                    ((SecureNioChannel) channel).reset(engine);
          38                }
           else {
          39                    channel.reset();
          40                }

          41            }

          42            // 這里就是將SocketChannel注冊到Poller了。
          43            // getPoller0用的循環的方式來返回Poller,即Poller 1, 2, 3 ... n 然后再回到1, 2, 3.
          44            getPoller0().register(channel);
          45        }
           catch (Throwable t) {
          46            ExceptionUtils.handleThrowable(t);
          47            try {
          48                log.error("", t);
          49            }
           catch (Throwable tt) {
          50                ExceptionUtils.handleThrowable(t);
          51            }

          52            // Tell to close the socket
          53            return false;
          54        }

          55        return true;
          56    }

          好了,終于到了Poller了,下一篇開始Poller。
          posted on 2010-12-07 09:32 臭美 閱讀(4597) 評論(0)  編輯  收藏 所屬分類: Tomcat
          主站蜘蛛池模板: 安新县| 东乌| 会同县| 镇安县| 临湘市| 惠水县| 隆化县| 盐亭县| 乃东县| 镇沅| 博湖县| 玉田县| 双辽市| 达拉特旗| 永春县| 沛县| 三河市| 桦南县| 乌兰浩特市| 五家渠市| 航空| 墨脱县| 扬州市| 崇文区| 醴陵市| 太谷县| 青神县| 华安县| 五寨县| 樟树市| 保亭| 定日县| 北票市| 岐山县| 阳曲县| 绥芬河市| 莱阳市| 信宜市| 永川市| 车致| 宝鸡市|