伺服器在調用<code>listen</code>和<code>accept</code>後,就會阻塞在<code>accept</code>函數上,<code>accpet</code>函數傳回後循環調用<code>accept</code>函數等待客戶的<code>tcp</code>連接配接。
我們知道伺服器段<code>listen</code>套接字能處理的連接配接數與監聽隊列的大小有關,如果這時候又大量的使用者并發發起<code>connec</code>連接配接,那麼在<code>listen</code>有隊列上限(最大可接受tcp的連接配接數)的情況下,有多少個<code>connect</code>會成功了。
試驗證明,當連接配接數遠遠高于<code>listen</code>的可連接配接數上限時,用戶端的大部分<code>tcp</code>請求會被抛棄,隻有當<code>listen</code>監聽隊列空閑或者放棄某個連接配接時,才可以接收新的連接配接
那麼我們當并發的連接配接數較大時,伺服器應該如何來避免這種情況出現?
用戶端如何模拟高并發呢?
用戶端運作初期完成所設定的一定量的<code>socket</code>建立和相應的處理線程的建立,然後使用條件變量來完成線程同步,直到最後一個線程建立完成,才向所有線程發出廣播通知,讓所有線程并發調用<code>connect</code>(這樣相當于所有的用戶端在同一時間請求連接配接,即高并發),連接配接成功則關閉連接配接,失敗則傳回。

關于 linux系統中程序所能建立的線程數目?
每個程序需要自己獨立的棧空間,linux預設的線程棧大小是10mb。在32位的機子上一個程序需要4g的記憶體空間,去掉自己的棧空間全局程式段空間,一般隻有3g記憶體可以用,建立線程時就需要從這3g的空間中配置設定10m出來,是以最多可以配置設定300多個線程。
當然這裡還可以使用多個程序,每個程序300個線程的方式來進一步擴大并發量。
當然64位系統中,此數值會不同,甚至會大很多
linux最大線程數限制及目前線程數查詢: 總結系統限制有: <code>cat /proc/sys/kernel/pid_max</code> #查系統支援的最大線程數,一般會很大,相當于理論值 <code>cat /proc/sys/kernel/threads-max</code> <code>max_user_process</code>(<code>ulimit -u</code>) #系統限制某使用者下最多可以運作多少程序或線程 <code>/proc/sys/vm/max_map_count</code> 硬體記憶體大小
我們在用戶端程式中,為我們模拟每個用戶端建立一個線程
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
條件變量是利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動作:一個線程等待”條件變量的條件成立”而挂起;另一個線程使”條件成立”(給出條件成立信号)。為了防止競争,條件變量的使用總是和一個互斥鎖結合在一起。
當然這裡有個關鍵問題,就是如何保證模拟出來所有的用戶端同步在連接配接伺服器之前
在這裡就使用條件變量進行同步。
條件變量初始化在connect之前設定條件變量,![]()
Linux下套接字詳解(七)----線程池accept處理高并發connect 前言 用戶端模拟高并發 伺服器處理大并發 代碼 注意:阻塞傳回後立即解鎖,防止互斥量加鎖帶來的阻塞 <code>pthread_mutex_unlock(&mut);</code>![]()
Linux下套接字詳解(七)----線程池accept處理高并發connect 前言 用戶端模拟高并發 伺服器處理大并發 代碼
當條件滿足時,喚醒阻塞在條件變量上的線程:
綜上,用戶端模拟并發過程中沒有存在不同步的情況導緻上述性能問題。(注意,在廣播的時候,會出現廣播丢失的情況,是以需要多次執行廣播操作才會使得所有線程執行任務,是以某種程度上這裡并不能模拟完完全全的并發)
[注意] 用戶端和伺服器之間的連接配接是在同一台機器上,使用socket方式通信的話會經過127.0.0.1的回環線路,不會有網卡等硬體資源的通路性能消耗,是以不存在網絡通信時延等問題。
性能問題主要發生在伺服器,可能是以下幾部分造成:
伺服器的監聽隊列<code>listen(listenfd,xxx)</code>
參數2指定隊列内所能容納的元素的最大值,
當來不及從隊列中移除元素時(調用accept移除或者tcp自動放棄)就會造成隊列滿而使得一些請求丢失。
解決辦法
增大隊列容量是一種辦法,但是注意等待隊列太會帶來效率的性能缺陷,而且<code>listen</code>函數對最大隊列容量有一個上限,大小為<code>somaxconn</code>,當然必要時刻我們可以修改這個常量的大小。
直接修改<code>listen</code>及相關函數的實作(比較麻煩,不建議),可以将<code>listen</code>所維護的隊列修改為<code>linklist</code>,支援隊列的動态增長。
<code>accept</code>導緻阻塞過長時間,使得隊列無法及時清空已經完成3次連接配接的<code>socket</code>
也就是任意兩個<code>accept</code>之間的時間間隔是關鍵因素,如下面代碼所示(一般來說,可以通過<code>listen</code>之後在循環調用<code>accept</code>來将已完成3次握手的連接配接從listen所維護的隊列中移除。
解決辦法:
* 兩個accept之間盡量不要又多餘的操作,使得accept傳回後可以立刻執行下一個accept。經過試驗,該方法可以較好的提高性能,減少connect的丢失數。
本質上這是一種“生産者-消費者”的模式,listen維護“已連接配接”和“待連接配接”的隊列,當客戶發出連接配接請求并最終連接配接成功時,在“已連接配接”隊列中會生産一個“product”,然後這時候希望“消費者”也就是accept函數可以快速的從隊列中消費這個“product”,這樣就不會因為隊列滿而導緻無法繼續生産(也就是客戶的connect會失效,導緻上面隊列長度10,300個并發connect帶來的67個存活的情況),但是在本例情況下,我們無法控制生産者的瘋狂生産行為,因為連接配接是客戶發起的,這是不可預知的,是以我們如果想不修改listen函數來提高性能的話,那麼就隻能讓消費者更加快的把産品消耗掉,使得listen隊列可以容納更多的新生産的産品,而第一種加快消費者消耗産品的方法就是a,第二種加快消費者消耗産品的方法是我們可以增加多幾個消費者來幫忙消耗,但是這幾個消費者間也要好好協調。第三種方法是讓消費者把産品先移走為listen的隊列騰出空間,再自行處理産品,如d所示。
使用多線程政策,每個線程獨立調用accept。(花了一個上午的時候正glib的線程池,一直用不了,其他都正常,就是線程不啟動,不知道會不會是bug)
修改listen和accept的實作方式,讓listen所維護的隊列可以智能的判斷擁擠情況,進而對accept的調用做出排程,在隊列繁忙時,使用多線程的方式讓多個accept來移除隊列中的元素,在隊列空閑時,可以适當的調整accept的處理線程數,這也是一種線程池的實作。
修改accept的實作方式,在accept中實作一個“消費緩沖區”,為的是及時将listen中的隊列元素移動到該緩沖區中,再由其他處理線程或者程序來對緩沖區中的元素進行處理,這個方法盡量讓listen隊列中已連接配接的socket可以被移除。這個方法比較上述方法來說是比較好的一種,但是還是需要修改已有的代碼。
伺服器短開辟一個線程池,線程完成與原來單線程的伺服器執行相同的操作,accept接受用戶端的請求,并且開辟線程進行響應進行
伺服器線程池
線程池中的線程完成同原來伺服器主線程完全相同的操作
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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
轉載:http://blog.csdn.net/gatieme/article/details/50828863