写在前面:对Netty并发问题的测试和解决完全超出了我的预期,想说的东西越来越多。所以才出现这个中篇,也就是说,一定会有下篇。至于问题点的发现,OneCoder也在努力验证中。
继续并发的问题。在《Java NIO框架Netty教程(十一)-并发访问测试(上)》中,我们测试的其实是一种伪并发的情景。底层实际只有一个Channel在运作,不会出现什么无响应的问题。而实际的并发不是这个意思的,独立的客户端,自然就会有独立的channel。也就是一个服务端很可能和很多的客户端建立关系,就有很多的Channel,进行了多次的绑定。下面我们来模拟一下这种场景。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226 1/**
2
3
4
5
6
7
8 * Netty并发,多connection,socket并发测试
9
10
11
12
13
14
15 *
16
17
18
19
20
21
22 * @author lihzh(OneCoder)
23
24
25
26
27
28
29 * @alia OneCoder
30
31
32
33
34
35
36 * @Blog http://www.coderli.com
37
38
39
40
41
42
43 * @date 2012-8-13 下午10:47:28
44
45
46
47
48
49
50 */
51
52
53
54
55
56 public
57 class
58 ConcurrencyNettyMultiConnection {
59
60
61
62
63
64
65
66
67
68
69
70
71 public
72 static
73 void
74 main(String args[]) {
75
76
77
78
79
80
81 final
82 ClientBootstrap bootstrap =
83 new
84 ClientBootstrap(
85
86
87
88
89
90
91 new
92 NioClientSocketChannelFactory(
93
94
95
96
97
98
99 Executors.newCachedThreadPool(),
100
101
102
103
104
105
106 Executors.newCachedThreadPool()));
107
108
109
110
111
112
113 // 设置一个处理服务端消息和各种消息事件的类(Handler)
114
115
116
117
118
119
120 bootstrap.setPipelineFactory(
121 new
122 ChannelPipelineFactory() {
123
124
125
126
127
128
129 @Override
130
131
132
133
134
135
136 public
137 ChannelPipeline getPipeline()
138 throws
139 Exception {
140
141
142
143
144
145
146 return
147 Channels.pipeline(
148 new
149 ObjectEncoder(),
150
151
152
153
154
155
156 new
157 ObjectClientHandler()
158
159
160
161
162
163
164 );
165
166
167
168
169
170
171 }
172
173
174
175
176
177
178 });
179
180
181
182
183
184
185 for
186 (
187 int
188 i =
189 0
190 ; i <
191 3
192 ; i++) {
193
194
195
196
197
198
199 bootstrap.connect(
200 new
201 InetSocketAddress(
202 "127.0.0.1"
203 ,
204 8000
205 ));
206
207
208
209
210
211
212 }
213
214
215
216
217
218
219 }
220
221
222
223
224
225 }
226
看到这段代码,你可能会疑问,这是在做什么。这个验证是基于酷壳的文章《Java NIO类库Selector机制解析(上)》。他是基于Java Selector直接进行的验证,Netty是在此之上封装了一层。其实OneCoder也做了Selector层的验证。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205 1/**
2
3
4
5
6
7
8 * Selector打开端口数量测试
9
10
11
12
13
14
15 *
16
17
18
19
20
21
22 * @author lihzh
23
24
25
26
27
28
29 * @alia OneCoder
30
31
32
33
34
35
36 * @blog http://www.coderli.com
37
38
39
40
41
42
43 */
44
45
46
47
48
49 public
50 class
51 ConcurrencySelectorOpen {
52
53
54
55
56
57
58
59
60
61
62
63
64 /**
65
66
67
68
69
70
71 * @author lihzh
72
73
74
75
76
77
78 * @alia OneCoder
79
80
81
82
83
84
85 * @throws IOException
86
87
88
89
90
91
92 * @throws InterruptedException
93
94
95
96
97
98
99 * @date 2012-8-15 下午1:57:56
100
101
102
103
104
105
106 */
107
108
109
110
111
112
113 public
114 static
115 void
116 main(String[] args)
117 throws
118 IOException, InterruptedException {
119
120
121
122
123
124
125 for
126 (
127 int
128 i =
129 0
130 ; i <
131 3
132 ; i++) {
133
134
135
136
137
138
139 Selector selector = Selector.open();
140
141
142
143
144
145
146 SocketChannel channel = SocketChannel.open();
147
148
149
150
151
152
153 channel.configureBlocking(
154 false
155 );
156
157
158
159
160
161
162 channel.connect(
163 new
164 InetSocketAddress(
165 "127.0.0.1"
166 ,
167 8000
168 ));
169
170
171
172
173
174
175 channel.register(selector, SelectionKey.OP_READ);
176
177
178
179
180
181
182 }
183
184
185
186
187
188
189 Thread.sleep(
190 300000
191 );
192
193
194
195
196
197
198 }
199
200
201
202
203
204 }
205
同样,通过Process Explorer去观察端口占用情况,开始跟酷壳大哥的观察到的效果一样。当不启动Server端,也就是不实际跟Server端建立链接的时候,3次selector open,占用了6个端口。
当启动Server端,进行绑定的时候,占用了9个端口
其实,多的三个,就是实际绑定的8000端口。其余就是酷壳大哥说的内部Loop链接。也就是说,对于我们实际场景,一次链接需要的端口数是3个。操作系统的端口数和资源当然有有限的,也就是说当我们增大这个链接的时候,错误必然会发生了。OneCoder尝试一下3000次的场景,并没有出现错误,不过这么下去出错肯定是必然的。
再看看服务端的情况,
这个还是直接通过Selector连接的时候的服务端的情况,除了两个内部回环端口以外,都是通过监听的8000的端口与外界通信,所以,服务端不会有端口限制的问题。不过,也可以看到,如果对链接不控制,服务端也维持大量的连接耗费系统资源!所以,良好的编码是多么的重要。
回到我们的主题,Netty的场景。先使用跟Selector测试一样的场景,单线程,一个bootstrap,连续多次connect看看错误和端口占用的情况。代码也就是开头提供的那段代码。
看看测试结果,
同样连接后还是占用9个端口。如果手动调用了channle.close()方法,则会释放与8000链接的端口,也就是变成6个端口占用。
增大连续连接数到10000。
首先没有报错,在每次close channel情况下,客户端端口占用情况如图(服务端情况类似)。
可见,并没有像selector那样无限的增加下去。这正是Netty中worker的巨大作用,帮我们管理这些琐碎的链接。具体分析,我们留待下章,您也可以自己研究一下。(OneCoder注:如果没关闭channel,会发现对8000端口的占用,会迅速增加。系统会很卡。)
调整测试代码,用一个线程来模拟一个客户端的请求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321 1/**
2
3
4
5
6
7
8 * Netty并发,多connection,socket并发测试
9
10
11
12
13
14
15 *
16
17
18
19
20
21
22 * @author lihzh(OneCoder)
23
24
25
26
27
28
29 * @alia OneCoder
30
31
32
33
34
35
36 * @Blog http://www.coderli.com
37
38
39
40
41
42
43 * @date 2012-8-13 下午10:47:28
44
45
46
47
48
49
50 */
51
52
53
54
55
56 public
57 class
58 ConcurrencyNettyMultiConnection {
59
60
61
62
63
64
65
66
67
68
69
70
71 public
72 static
73 void
74 main(String args[]) {
75
76
77
78
79
80
81 final
82 ClientBootstrap bootstrap =
83 new
84 ClientBootstrap(
85
86
87
88
89
90
91 new
92 NioClientSocketChannelFactory(
93
94
95
96
97
98
99 Executors.newCachedThreadPool(),
100
101
102
103
104
105
106 Executors.newCachedThreadPool()));
107
108
109
110
111
112
113 // 设置一个处理服务端消息和各种消息事件的类(Handler)
114
115
116
117
118
119
120 bootstrap.setPipelineFactory(
121 new
122 ChannelPipelineFactory() {
123
124
125
126
127
128
129 @Override
130
131
132
133
134
135
136 public
137 ChannelPipeline getPipeline()
138 throws
139 Exception {
140
141
142
143
144
145
146 return
147 Channels.pipeline(
148 new
149 ObjectEncoder(),
150
151
152
153
154
155
156 new
157 ObjectClientHandler()
158
159
160
161
162
163
164 );
165
166
167
168
169
170
171 }
172
173
174
175
176
177
178 });
179
180
181
182
183
184
185 for
186 (
187 int
188 i =
189 0
190 ; i <
191 1000
192 ; i++) {
193
194
195
196
197
198
199 // 连接到本地的8000端口的服务端
200
201
202
203
204
205
206 Thread t =
207 new
208 Thread(
209 new
210 Runnable() {
211
212
213
214
215
216
217 @Override
218
219
220
221
222
223
224 public
225 void
226 run() {
227
228
229
230
231
232
233 bootstrap.connect(
234 new
235 InetSocketAddress(
236 "127.0.0.1"
237 ,
238 8000
239 ));
240
241
242
243
244
245
246 try
247 {
248
249
250
251
252
253
254 Thread.sleep(
255 300000
256 );
257
258
259
260
261
262
263 }
264 catch
265 (InterruptedException e) {
266
267
268
269
270
271
272 e.printStackTrace();
273
274
275
276
277
278
279 }
280
281
282
283
284
285
286 }
287
288
289
290
291
292
293 });
294
295
296
297
298
299
300 t.start();
301
302
303
304
305
306
307 }
308
309
310
311
312
313
314 }
315
316
317
318
319
320 }
321
不出所料,跟微博上问我问题的朋友出现的问题应该是一样的:
Write: 973
Write: 974
八月 17, 2012 9:57:28 下午 org.jboss.netty.channel.SimpleChannelHandler
警告: EXCEPTION, please implement one.coder.netty.chapter.eight.ObjectClientHandler.exceptionCaught() for proper handling.
java.net.ConnectException: Connection refused: no further information
这个问题,笔者确实尚未分析出原因,仅从端口占用情况来看,跟前面的测试用例跑出的效果基本类似。应该说明不是端口不足导致的,不过OneCoder尚不敢妄下结论。待研究后,我们下回分解吧:)您有任何想法,也可以提供给我,我来进行验证。
如非特别注明,本站内容均为OneCoder原创,转载请务必注明作者和原始出处。
本文地址:链接地址