昨天優化了一下服務器的網絡部分,測試了一下,在不考慮吞吐率的情況下,并發5W
下面是俺的一些經驗
1.基本結構
服務器結構如下圖

使用2個不同的線程池來分別來處理 網絡數據包的發送接收 以及 消息的處理.
這樣可以避免繁重的業務處理導致網絡數據包接收的阻塞.
根據服務器CPU情況創建線程. 服務器是2*4核心. 即雙CPU, 每CPU上有4個核心.
在邏輯上就有8個處理單元.
在第1個CPU上的每個核心上創建x個線程用于發送和接收.
即: 發送接收線程池有線程數 x*4個,位于第1個CPU上.
在第2個CPU上的每個核心上創建y個線程,用于業務處理
即: 業務處理線程池有線程數 y*4個,位于第2個CPU上
具體的x,y應該按照實際的系統設置.設置的原則是:
1. 盡量小的線程上下文切換開銷
2. 盡量高的CPU利用率(注意,是利用率,不是占用率)
一般來說,y>x
BOOL?WINAPI?SetProcessAffinityMask(
??__in??????????HANDLE?hProcess,
??__in??????????DWORD_PTR?dwProcessAffinityMask
);
//
?此API用于設置進程的CPU親緣屬性,第2個參數是"位或"表示.?對于2*4核系統,則設置位0xFF
DWORD_PTR?WINAPI?SetThreadAffinityMask(
??__in??????????HANDLE?hThread,
??__in??????????DWORD_PTR?dwThreadAffinityMask
);
//
?此API用于設置線程的CPU親緣屬性,第2個參數是"位或"表示.?需要注意的是,dwThreadAffinityMask必須是dwProcessAffinityMask的子集
DWORD?WINAPI?SetThreadIdealProcessor(
??__in??????????HANDLE?hThread,
??__in??????????DWORD?dwIdealProcessor
);
//
?此API用于設置線程的首選CPU,操作系統在調度線程時優先考慮首選核心,?第2個參數是以0為基數的處理器ID
上述3個API都可以用來設置線程的執行單元是哪個. 一般來說,線程調度是由操作系統負責.人為的控制有時候反而會降低效率.但針對高負荷的線程處理,完全可以指定獨立的CPU來優化.
比如,設定dwThreadAffinityMask=0xF,表示此線程在1-4核上執行,具體是哪個核還是由操作系統調度.這樣可以將不同用途的線程分配到不同的CPU上,因為每個CPU有自己獨立的L2 Cache,這樣做可以避免不同類型線程在不同CPU之間切換帶來的損失.
參考: 面向共享高速緩存多核系統的軟件技術
上面所說的這些都只是理論,到實際的系統中,必須經過反復的性能對比試驗來確定最佳方案
2. 登陸數據的接收
在IOCP中,經常使用AcceptEx來投遞前置式的accept請求. 這里有一個問題: 登陸數據如何接收?
一般來說,登陸數據包是連接后的第1個或者第2個數據包.必須先保證一個TCP連接是信任的才能進行業務處理.有下列方法
方法1:
如果登陸數據包是第一個客戶端發送的數據包,那么AcceptEx的dwReceiveDataLength 可以設置成登陸數據包大小,AcceptEx只有在收到此數據包后才投遞到完成隊列進行驗證
方法2:
AcceptEx的dwReceiveDataLength 設置成0,表示在接收到連接后立即投遞到完成隊列.在完成隊列中,投遞WSARecv獲得登陸數據包
方法3:
AcceptEx的dwReceiveDataLength 設置成0,表示在接收到連接后立即投遞到完成隊列.在Accept時,使用阻塞的recv來接收登陸數據包,如果recv超時,則踢掉連接
這3種方式沒有哪種能夠徹底的解決D.O.S的問題,只能在一定程度上緩解.
方法1:客戶端只連接不發送數據,大量的這種連接會導致拒絕服務(一般采用附加線程定時檢測超時)
方法2:客戶端只連接不發送數據,大量的這種連接會導致拒絕服務(一般采用附加線程定時檢測超時)
方法3:recv的超時時間設置很敏感,過大的超時時間同樣會因為大量連接而拒絕服務(recv超時時間設定得如果合適是能夠在一定程度上緩解)
3. 設置LINGER縮短連接關閉時間
//
?éè??SO_DONTLINGER
BOOL?bDontLinger?
=
?FALSE;
::setsockopt(?lpWsaOverlappedPlus
->
hSocket
????,?SOL_SOCKET
????,?SO_DONTLINGER
????,?(
const
?
char
*
)
&
bDontLinger
????,?sizeof(BOOL)
????);

//
?éè??SO_LINGER
linger?stLinger;
stLinger.l_onoff?
=
?
1
;
stLinger.l_linger?
=
?
0
;
::setsockopt(?lpWsaOverlappedPlus
->
hSocket
????,?SOL_SOCKET
????,?SO_LINGER
????,?(CHAR
*
)
&
stLinger
????,?sizeof(linger)
????);
4. 修改注冊表,修改TCP參數,具體的含義可以查閱MSDN
Windows?Registry?Editor?Version?
5.00
[
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Performance
]
"
MaxUserPort
"
=
dword:0000fffe

[
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
]
"
TCPFinWait2Delay?
"
=
dword:0000001e
"
SynAttackProtect
"
=
dword:
1
?
"
TcpMaxPortsExhausted
"
=
dword:
5
?
"
TcpMaxHalfOpen
"
=
dword:
500
?
"
TcpMaxHalfOpenRetried
"
=
dword:
400
?
"
TcpMaxConnectResponseRetransmissions
"
=
dword:
2
?
"
TcpMaxDataRetransmissions
"
=
dword:
2
?
"
EnablePMTUDiscovery
"
=
dword:
0
?
"
KeepAliveTime
"
=
dword:
300000
?
"
NoNameReleaseOnDemand
"
=
dword:
1
?
"
DefaultTTL
"
=
dword:
256
?
"
EnableDeadGWDetect
"
=
dword:
0
?
"
DisableIPSourceRouting
"
=
dword:
1
?
"
EnableFragmentChecking
"
=
dword:
1
?
"
EnableMulticastForwarding
"
=
dword:
0
?
"
IPEnableRouter
"
=
dword:
0
?
"
EnableAddrMaskReply
"
=
dword:
0
?
"
TcpTimedWaitDelay
"
=
dword:0000001e

[
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters
]
?
"
EnableICMPRedirect
"
=
dword:
0
?
"
EnableDynamicBacklog
"
=
dword:
1
?
"
MinimumDynamicBacklog
"
=
dword:
20
?
"
MaximumDynamicBacklog
"
=
dword:
20000
?
"
DynamicBacklogGrowthDelta
"
=
dword:
10
?