LF模式是个坑,ZeroIce中间件让你体会这个痛
LF模式是個坑,一個小小的失誤就可能使你的網(wǎng)絡(luò)處理癱瘓,Ice就很好地展現(xiàn)了出來,換句話說,Ice中間件或是LF模式就是一個坑,如果你一不小心。
LF模式的官方論文中,論述了此模式用于高性能網(wǎng)絡(luò)并發(fā)模式,使用的是系統(tǒng)的隱式隊列,也就是Reactor復(fù)用多路IO,(如果是select的話,還是會將事件收集到一個顯式隊列),每次只有一條線程可以有一次機會成為leader訪問這個隊列,從隊列取出事件后放棄leader,同時喚醒另一線程(如果還有follower線程的話);注意這時的線程既不是leader也不是follower,它就可以處理事件,一般就是同步非阻塞讀寫,然后處理這個請求(通常來說就是dispatchFromThisThread)。當上面的工作結(jié)束就,才會作為follower等待成為leader,如果沒有l(wèi)eader的話就會馬上成為leader。在這種模式中,稱為LF模式有點誤導(dǎo),因為實際上是三種狀態(tài),LPF,Leader,Process,Follower,狀態(tài)機為 L->P->F->L->...。Follower狀態(tài)的線程阻塞等待著Leader離開Leader狀態(tài)去Process,從而喚醒可能的Follower狀態(tài)的線程。被喚醒的Follower線程狀態(tài)變?yōu)長eader,成為線程池中唯一當前有權(quán)操作隊列的線程,要么阻塞在空隊列,要么取出隊列一個事件離開Leader狀態(tài)去Process。線程池線程當Process完事件后才會狀態(tài)變?yōu)镕ollower,或者阻塞等待Leader的喚醒,或者自動成為Leader。狀態(tài)機就為 L(block on event queue)->P->F->(block on followers queue)->L ...。在這種模式下的線程池中,最多只有一個線程在Leader狀態(tài),并且只有這個Leader線程可以阻塞在IO事件隊列,其它線程要么在Process狀態(tài)處理事件,要么就是在Follower狀態(tài)等待成為下一個Leader。當沒有IO事件的時候,就只有一個線程在Leader狀態(tài)阻塞在IO事件隊列,其它線程都結(jié)事了事件的處理,并在Follower狀態(tài)阻塞等待Leader線程釋放信號。
通常用來襯托LF模式的,就是sync/async模式,并且都會舉例Manager-Workers線程池。Manager負責將隊列的事件指派到空閑Worker線程進行處理。Worker線程被喚醒處理完事件后再次阻塞等待Manager喚醒。當沒有事件的時候,Manager阻塞在事件隊列,Worker線程阻塞等待Manager線程喚醒。這種線程池有一個固定線程去阻塞在事件隊列。并且每次Manager喚醒Worker都要通過堆來傳遞事件。(Manager從事件隊列取出一個事件寫入到堆內(nèi)存,Worker從堆內(nèi)存讀到自己的棧,然后處理棧上的這個事件;而Leader從事件隊列將一個事件讀到自己的棧,再就喚醒其它Follower,然后處理棧上的這個事件。)當Manager線程不負責Reactor復(fù)用多路IO的情況,在空閑時發(fā)生了一次IO事件必須跨線程寫入Manager線程的事件隊列,并喚醒Manager線程,然后Manager線程喚醒Worker線程去處理事件。而LF模式線程池,Leader從系統(tǒng)的多路IO復(fù)用分離函數(shù)中返回,喚醒一個Follower,然后自己去處理事件。這樣一比較就是Manager-Workers線程池進行了兩次線程喚醒,而LF模式線程池只有一個線程喚醒(這里必須要公正,Leader是之前就被喚醒經(jīng)歷消耗了一次切換),事件在Manager-Workers線程池需要多次拷貝。
那么為什么LF模式不心就會踩坑,而Ice的設(shè)計就讓體會這個坑。
問題在于如果LF模式線程池的線程進行Process阻塞等待IO響應(yīng),而所有的線程都在Process過程中阻塞等待IO響應(yīng),更重要這些被等待的IO應(yīng)用在這個線程池的Reactor,就會再也沒有線程成為Leader去多路IO分離函數(shù)中讀取IO事件。這時候這個LF線程池就會癱瘓不工作。Ice中間件會讓你深深體會這種痛。Ice采用ActiveObject模式進行OBR對象代理請求。控制線程調(diào)用proxy請求返回一個future,阻塞等待future。Communicator的clientThreadPool負責Reactor,收到請求的response后就向future發(fā)信號,從而喚醒這個response對應(yīng)的future阻塞住的控制線程。這種情況下使用ORB對象代理請求的線程與網(wǎng)絡(luò)Reactor線程池獨立,負責Reactor的LF線程池不會被其它邏輯影響。但是在LF線程池中進行ORB對象代理請求呢?問題就來了。你的LF線程池隨時都可能癱瘓掉,只要你不小心。極端地,LF線程池只有一個線程,這個線程在Process事件時,進行了ORB對象代理請求,阻塞等待future。Good Job!! 這個線程池中唯一的線程就永遠不會再有機會成為Leader去取出遠端的response的IO事件,去喚醒這個阻塞住線程的future了。這還不容易解決,都說是線程池,那會只有一個線程的呢。我們讓這個LF線程池添加到兩個線程,第一個線程取出事件喚醒第二個線程,然后自己處理事件時,進行了ORB對象代理請求,阻塞等待future;第二個線程成為Leader阻塞在多路IO分離函數(shù),并在遠端response到來時,從分離函數(shù)返回,得以喚醒了阻塞第一個線程的future。但是很不幸,第二個線程在等待到response到來之前,收到其它IO事件,而處理這個事件卻進行了ORB對象代理請求,阻塞等待future。汗,LF線程池都被阻塞在future,future又等待IO事件。再往下演繹,不論LF線程池有多少線程,只要你的處理邏輯中進行了同步阻塞的ORB對象代理請求,都會使你的Ice網(wǎng)絡(luò)處理癱瘓,癱瘓的原因是LF線程池癱瘓了。Ice的Server端線程池默認就在當前線程進行請求的dispatch,如果你在實現(xiàn)你的服務(wù)的時候必須依賴其它ORB對象代理請求時,你就要小心了,你可能因為這樣而阻塞掉所有Communicator的Server端LF線程池,至使網(wǎng)絡(luò)Reactor癱瘓。如果你使用了bidirection connection來提供callback的飼服,要是你在callback的實現(xiàn)中依賴了其它ORB對象代理請求時,你同樣也要小心了,你可能因為這樣而阻塞掉所有Communicator的Client端LF線程池,至使網(wǎng)絡(luò)Reactor癱瘓。Ice為了避免,會默認為每個連接加上一個計時器,讓連接自動斷開。但是我們還是有應(yīng)用場合希望連接長九不斷開,關(guān)閉這種機制,這時就要小心了。再者就是,即使你所有的ORB對象請求代理都用異步方式(ami,amd)進行編程,但是悲劇的是你無法干涉到你依賴的其它人函數(shù)沒有進行同步阻塞的ORB對象代理請求。
轉(zhuǎn)載于:https://www.cnblogs.com/bbqzsl/p/7462606.html
總結(jié)
以上是生活随笔為你收集整理的LF模式是个坑,ZeroIce中间件让你体会这个痛的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 替换字符串
- 下一篇: js中this的指向问题