当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
}

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
}

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
}

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

protected
boolean
setSocketOptions(SocketChannel socket)
{2
//
Process the connection
3

try
{4
//
disable blocking, APR style, we are gonna be polling it5
//
这里终于看到了印象中的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
本文解析了Tomcat使用NIO时的启动流程,重点介绍了NioEndPoint的启动过程,包括初始化ServerSocketChannel、创建线程池、启动Poller及Acceptor线程,并详细分析了Acceptor的工作原理。
491

被折叠的 条评论
为什么被折叠?



