數(shù)據(jù)包經(jīng)主線程收包后投遞到邏輯處理線程
我們利用Linux reuseaddr和connect以后的udp fd特性,只有第一個數(shù)據(jù)包經(jīng)主線程收包后投遞到邏輯處理線程,其它數(shù)據(jù)包直接在邏輯線程接收。
我們具體是怎么做的?
首先主線程還是創(chuàng)建一個fd,綁定7000端口。當然這里面有一個關(guān)鍵點是需要設(shè)置套接字的選項——reuseaddr,這個套接字選項在TCP領(lǐng)域用的很多,在UDP領(lǐng)域中大家可能還比較少接觸。同樣在主線監(jiān)聽可讀事件。主線程開始收包了,同樣的流程,收到到第一個stun包,獲取WebRTC的web address(IP+端口)以及區(qū)分會議的RoomID和區(qū)分與會者的UserID。使用Hash(RoomID)到一個邏輯處理的子線程,到此為止流程與之前的多線程方案沒有太大區(qū)別。
在邏輯處理子線程里面的方案就有一定的技巧了。它首先會在這個子線程里面再創(chuàng)建一個fd,這個fd監(jiān)聽的端口和主線程是一樣的,也是7000端口。因為它設(shè)置了reuseaddr,所以說它的綁定可以成功,也就是說這個7000端口可以在多個線程里面同時監(jiān)聽。
接下來的流程就比較重要了,我們有了這個fd之后,我們需要做一次connect, connect在UDP領(lǐng)域大家使用得不多,但是它connect之后有什么效果呢?
大家可以閱讀《UNIX網(wǎng)絡(luò)編程 卷1第3版》的8.11,簡單概括:UDP fd connect之后,會綁定本端與對端的四元組。當內(nèi)核選擇fd來收udp包時采用的是最佳匹配的原則。
因此connect過后的fd是四元組匹配最高的,這時內(nèi)核會直接選擇我們在子線程里面創(chuàng)建的fd。這個方案有點繞,但是還是比較有意思的,涉及到一些內(nèi)核的事情,推薦大家感興趣可以深入研究一下。在邏輯處理子線程中,我們還是會把這個新創(chuàng)建的fd注冊到我們的事件循環(huán)里面。此后這個用戶的上下行數(shù)據(jù)的IO操作將直接在這個邏輯處理線程。
這個方案就是我們優(yōu)化后的Server的線程方案,它可以最大程度的降低跨線程的調(diào)用與加鎖,整體的性能和代碼的可讀性也都會變高。