随笔-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 臭美 阅读(4578) 评论(0)  编辑  收藏 所属分类: Tomcat

只有注册用户登录后才能发表评论。


网站导航: