1
- # Table of Contents
2
-
1
+ # 目录
3
2
* [ 一、I/O复用模型解读] ( #一、io复用模型解读 )
4
3
* [ 二、TOMCAT对IO模型的支持] ( #二、tomcat对io模型的支持 )
5
4
* [ 三、TOMCAT中NIO的配置与使用] ( #三、tomcat中nio的配置与使用 )
9
8
* [ 七、关于性能] ( #七、关于性能 )
10
9
* [ 八、总结] ( #八、总结 )
11
10
12
-
13
11
本文转自:http://www.sohu.com/a/203838233_827544
14
12
15
13
本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看
37
35
38
36
Tomcat的NIO是基于I/O复用来实现的。对这点一定要清楚,不然我们的讨论就不在一个逻辑线上。下面这张图学习过I/O模型知识的一般都见过,出自《UNIX网络编程》,I/O模型一共有阻塞式I/O,非阻塞式I/O,I/O复用(select/poll/epoll),信号驱动式I/O和异步I/O。这篇文章讲的是I/O复用。
39
37
40
- ![ ] ( http://5b0988e595225.cdn.sohucs.com/images/20171112/0ab0b70be5a64b0e9014de867235da73.png )
41
-
38
+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405104442.png )
42
39
IO复用.png
43
40
44
41
这里先来说下用户态和内核态,直白来讲,如果线程执行的是用户代码,当前线程处在用户态,如果线程执行的是内核里面的代码,当前线程处在内核态。更深层来讲,操作系统为代码所处的特权级别分了4个级别。
@@ -51,13 +48,12 @@ IO复用.png
51
48
52
49
上面提到的网络事件有连接就绪,接收就绪,读就绪,写就绪四个网络事件。I/O复用主要是通过Selector复用器来实现的,可以结合下面这个图理解上面的叙述。
53
50
54
- ![ ] ( http://5b0988e595225.cdn.sohucs.com/images/20171112/3a18d371c9c848479ca39d8eb4e57ce4.jpeg )
55
-
51
+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405104504.png )
56
52
Selector图解.png
57
53
58
54
## 二、TOMCAT对IO模型的支持
59
55
60
- ![ ] ( http ://5b0988e595225.cdn.sohucs .com/images/20171112/983c122c2a924e01b9f898279e2bd0b5.jpeg )
56
+ ![ ] ( https ://java-tutorial.oss-cn-shanghai.aliyuncs .com/20230405104517.png )
61
57
62
58
tomcat支持IO类型图.png
63
59
@@ -71,7 +67,7 @@ tomcat从6以后开始支持NIO模型,实现是基于JDK的java.nio包。这
71
67
72
68
## 四、NioEndpoint组件关系图解读
73
69
74
- ![ ] ( http ://5b0988e595225.cdn.sohucs .com/images/20171112/4e1a924a60974c76ab5115007562e563.jpeg )
70
+ ![ ] ( https ://java-tutorial.oss-cn-shanghai.aliyuncs .com/20230405104543.png )
75
71
76
72
tomcatnio组成.png
77
73
@@ -87,8 +83,7 @@ LimitLatch是连接控制器,它负责维护连接数的计算,nio模式下
87
83
88
84
## 五、NioEndpoint执行序列图
89
85
90
- ![ ] ( http://5b0988e595225.cdn.sohucs.com/images/20171112/d69c2ef5110d4706aa7284c616d62927.jpeg )
91
-
86
+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405104621.png )
92
87
tomcatnio序列图.png
93
88
94
89
在下一小节NioEndpoint源码解读中我们将对步骤1-步骤11依次找到对应的代码来说明。
@@ -99,8 +94,7 @@ tomcatnio序列图.png
99
94
100
95
无论是BIO还是NIO,开始都会初始化连接限制,不可能无限增大,NIO模式下默认是10000。
101
96
102
- ![ ] ( http://5b0988e595225.cdn.sohucs.com/images/20171112/332338f6d2c8488e9b35cd4fd76f078a.png )
103
-
97
+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405104637.png )
104
98
** 6.2、步骤解读**
105
99
106
100
下面我们着重叙述跟NIO相关的流程,共分为11个步骤,分别对应上面序列图中的步骤。
@@ -111,46 +105,41 @@ tomcatnio序列图.png
111
105
112
106
Socket,NIO下这里返回的是SocketChannel。
113
107
114
- ![ ] ( http://5b0988e595225.cdn.sohucs.com/images/20171112/ca01395590944a35b4717b15c984849e.png )
115
-
108
+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405104855.png )
116
109
** 步骤2** :启动接收线程
117
110
118
- ![ ] ( http://5b0988e595225.cdn.sohucs.com/images/20171112/e7c40dc6f84b48f4915ca854c2a3b2cc.png )
119
111
120
112
** 步骤3** :ServerSocketChannel.accept()接收新连接
121
113
122
- ![ ] ( http ://5b0988e595225.cdn.sohucs .com/images/20171112/50ddbfe042514ba29a8bcd606b125f76.jpeg )
114
+ ![ ] ( https ://java-tutorial.oss-cn-shanghai.aliyuncs .com/20230405104937.png )
123
115
124
116
** 步骤4** :将接收到的链接通道设置为非阻塞
125
117
126
118
** 步骤5** :构造NioChannel对象
127
119
128
120
** 步骤6** :register注册到轮询线程
129
121
130
- ![ ] ( http ://5b0988e595225.cdn.sohucs .com/images/20171112/f3f5130bcf7a4348bc9752bd009c1b72 .png)
122
+ ![ ] ( https ://java-tutorial.oss-cn-shanghai.aliyuncs .com/20230405104957 .png)
131
123
132
124
** 步骤7** :构造PollerEvent,并添加到事件队列
133
125
134
- ![ ] ( http ://5b0988e595225.cdn.sohucs .com/images/20171112/65de3c5cebea42c59ff19e8a168cf406 .png)
126
+ ![ ] ( https ://java-tutorial.oss-cn-shanghai.aliyuncs .com/20230405105014 .png)
135
127
136
128
** 步骤8** :启动轮询线程
137
129
138
- ![ ] ( http ://5b0988e595225.cdn.sohucs .com/images/20171112/5c097feaa2324a2da082a81287f9e862 .png)
130
+ ![ ] ( https ://java-tutorial.oss-cn-shanghai.aliyuncs .com/20230405105027 .png)
139
131
140
132
** 步骤9** :取出队列中新增的PollerEvent并注册到Selector
141
133
142
- ![ ] ( http ://5b0988e595225.cdn.sohucs .com/images/20171112/8ac5e80b9aa84f57b4c3f7ecd4e2ea1d .png)
134
+ ![ ] ( https ://java-tutorial.oss-cn-shanghai.aliyuncs .com/20230405105043 .png)
143
135
144
136
** 步骤10** :Selector.select()
145
137
146
- ![ ] ( http://5b0988e595225.cdn.sohucs.com/images/20171112/82d20de2a4614eab9fe14e58234db552.jpeg )
147
-
148
- ![ ] ( http://5b0988e595225.cdn.sohucs.com/images/20171112/75641a4b8d444f8ab7a033bee9497c2b.png )
138
+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405105057.png )
149
139
140
+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405105110.png )
150
141
** 步骤11** :根据选择的SelectionKey构造SocketProcessor提交到请求处理线程
151
-
152
- ![ ] ( http://5b0988e595225.cdn.sohucs.com/images/20171112/b8ec9bbcbdcd4088841741acb20152f8.png )
153
-
142
+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405105154.png )
154
143
** 6.3、NioBlockingSelector和BlockPoller介绍**
155
144
156
145
上面的序列图有个地方我没有描述,就是NioSelectorPool这个内部类,是因为在整体理解tomcat的nio上面在序列图里面不包括它更好理解。
@@ -159,13 +148,13 @@ Socket,NIO下这里返回的是SocketChannel。
159
148
160
149
以执行servlet后,得到response,往socket中写数据为例,最终写的过程调用NioBlockingSelector的write方法。代码如下:
161
150
162
- ![ ] ( http ://5b0988e595225.cdn.sohucs .com/images/20171112/4ac6ac8fdb0a4b24bdcb646a09cbb13e.jpeg )
151
+ ![ ] ( https ://java-tutorial.oss-cn-shanghai.aliyuncs .com/20230405105223.png )
163
152
164
- ![ ] ( http ://5b0988e595225.cdn.sohucs .com/images/20171112/549283c8059b4e3c8798077b654dc3d1 .png)
153
+ ![ ] ( https ://java-tutorial.oss-cn-shanghai.aliyuncs .com/20230405105237 .png)
165
154
166
155
也就是说当socket.write()返回0时,说明网络状态不稳定,这时将socket注册OP_WRITE事件到辅Selector,由BlockPoller线程不断轮询这个辅Selector,直到发现这个socket的写状态恢复了,通过那个倒数计数器,通知Worker线程继续写socket动作。看一下BlockSelector线程的代码逻辑:
167
156
168
- ![ ] ( http ://5b0988e595225.cdn.sohucs .com/images/20171112/626817ca25fc4a439dbf5d23c2c08d0e.jpeg )
157
+ ![ ] ( https ://java-tutorial.oss-cn-shanghai.aliyuncs .com/20230405105251.png )
169
158
170
159
使用这个辅Selector主要是减少线程间的切换,同时还可减轻主Selector的负担。
171
160
@@ -175,13 +164,9 @@ Socket,NIO下这里返回的是SocketChannel。
175
164
176
165
NIO的优势更在于用少量的线程hold住大量的连接。还有一点,我们在压测的过程中,遇到在NIO模式下刚开始的一小段时间内容,会有错误,这是因为一般的压测工具是基于一种长连接,也就是说比如模拟1000并发,那么同时建立1000个连接,下一时刻再发送请求就是基于先前的这1000个连接来发送,还有TOMCAT的NIO处理是有POLLER线程来接管的,它的线程数一般等于CPU的核数,如果一瞬间有大量并发过来,POLLER也会顿时处理不过来。
177
166
178
- ![ ] ( http://5b0988e595225.cdn.sohucs.com/images/20171112/7d62f8792e1c41f090d945c755bba6c7.jpeg )
179
-
180
- 压测1.jpeg
181
-
182
- ![ ] ( http://5b0988e595225.cdn.sohucs.com/images/20171112/e8ec450685d64e5785db44a0bb60660c.jpeg )
167
+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405105304.png )
183
168
184
- 压测2.jpeg
169
+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230405105318.png )
185
170
186
171
## 八、总结
187
172
0 commit comments