LF模式是個坑,ZeroIce中間件讓你體會這個痛

LF模式是個坑,一個小小的失誤就可能使你的網絡處理癱瘓,Ice就很好地展示了出來,換句話說,Ice中間件或是LF模式就是一個坑,若是你一不當心。編程

LF模式的官方論文中,論述了此模式用於高性能網絡併發模式,使用的是系統的隱式隊列,也就是Reactor複用多路IO,(若是是select的話,仍是會將事件收集到一個顯式隊列),每次只有一條線程能夠有一次機會成爲leader訪問這個隊列,從隊列取出事件後放棄leader,同時喚醒另外一線程(若是還有follower線程的話);注意這時的線程既不是leader也不是follower,它就能夠處理事件,通常就是同步非阻塞讀寫,而後處理這個請求(一般來講就是dispatchFromThisThread)。當上面的工做結束就,纔會做爲follower等待成爲leader,若是沒有leader的話就會立刻成爲leader。在這種模式中,稱爲LF模式有點誤導,由於其實是三種狀態,LPF,Leader,Process,Follower,狀態機爲 L->P->F->L->...。Follower狀態的線程阻塞等待着Leader離開Leader狀態去Process,從而喚醒可能的Follower狀態的線程。被喚醒的Follower線程狀態變爲Leader,成爲線程池中惟一當前有權操做隊列的線程,要麼阻塞在空隊列,要麼取出隊列一個事件離開Leader狀態去Process。線程池線程當Process完事件後纔會狀態變爲Follower,或者阻塞等待Leader的喚醒,或者自動成爲Leader。狀態機就爲 L(block on event queue)->P->F->(block on followers queue)->L ...。在這種模式下的線程池中,最多隻有一個線程在Leader狀態,而且只有這個Leader線程能夠阻塞在IO事件隊列,其它線程要麼在Process狀態處理事件,要麼就是在Follower狀態等待成爲下一個Leader。當沒有IO事件的時候,就只有一個線程在Leader狀態阻塞在IO事件隊列,其它線程都結事了事件的處理,並在Follower狀態阻塞等待Leader線程釋放信號。網絡

一般用來陪襯LF模式的,就是sync/async模式,而且都會舉例Manager-Workers線程池。Manager負責將隊列的事件指派到空閒Worker線程進行處理。Worker線程被喚醒處理完事件後再次阻塞等待Manager喚醒。當沒有事件的時候,Manager阻塞在事件隊列,Worker線程阻塞等待Manager線程喚醒。這種線程池有一個固定線程去阻塞在事件隊列。而且每次Manager喚醒Worker都要經過堆來傳遞事件。(Manager從事件隊列取出一個事件寫入到堆內存,Worker從堆內存讀到本身的棧,而後處理棧上的這個事件;而Leader從事件隊列將一個事件讀到本身的棧,再就喚醒其它Follower,而後處理棧上的這個事件。)當Manager線程不負責Reactor複用多路IO的狀況,在空閒時發生了一次IO事件必須跨線程寫入Manager線程的事件隊列,並喚醒Manager線程,而後Manager線程喚醒Worker線程去處理事件。而LF模式線程池,Leader從系統的多路IO複用分離函數中返回,喚醒一個Follower,而後本身去處理事件。這樣一比較就是Manager-Workers線程池進行了兩次線程喚醒,而LF模式線程池只有一個線程喚醒(這裏必需要公正,Leader是以前就被喚醒經歷消耗了一次切換),事件在Manager-Workers線程池須要屢次拷貝。併發

那麼爲何LF模式不心就會踩坑,而Ice的設計就讓體會這個坑。異步

問題在於若是LF模式線程池的線程進行Process阻塞等待IO響應,而全部的線程都在Process過程當中阻塞等待IO響應,更重要這些被等待的IO應用在這個線程池的Reactor,就會再也沒有線程成爲Leader去多路IO分離函數中讀取IO事件。這時候這個LF線程池就會癱瘓不工做。Ice中間件會讓你深深體會這種痛。Ice採用ActiveObject模式進行OBR對象代理請求。控制線程調用proxy請求返回一個future,阻塞等待future。Communicator的clientThreadPool負責Reactor,收到請求的response後就向future發信號,從而喚醒這個response對應的future阻塞住的控制線程。這種狀況下使用ORB對象代理請求的線程與網絡Reactor線程池獨立,負責Reactor的LF線程池不會被其它邏輯影響。可是在LF線程池中進行ORB對象代理請求呢?問題就來了。你的LF線程池隨時均可能癱瘓掉,只要你不當心。極端地,LF線程池只有一個線程,這個線程在Process事件時,進行了ORB對象代理請求,阻塞等待future。Good Job!! 這個線程池中惟一的線程就永遠不會再有機會成爲Leader去取出遠端的response的IO事件,去喚醒這個阻塞住線程的future了。這還不容易解決,都說是線程池,那會只有一個線程的呢。咱們讓這個LF線程池添加到兩個線程,第一個線程取出事件喚醒第二個線程,而後本身處理事件時,進行了ORB對象代理請求,阻塞等待future;第二個線程成爲Leader阻塞在多路IO分離函數,並在遠端response到來時,從分離函數返回,得以喚醒了阻塞第一個線程的future。可是很不幸,第二個線程在等待到response到來以前,收到其它IO事件,而處理這個事件卻進行了ORB對象代理請求,阻塞等待future。汗,LF線程池都被阻塞在future,future又等待IO事件。再往下演繹,不論LF線程池有多少線程,只要你的處理邏輯中進行了同步阻塞的ORB對象代理請求,都會使你的Ice網絡處理癱瘓,癱瘓的緣由是LF線程池癱瘓了。Ice的Server端線程池默認就在當前線程進行請求的dispatch,若是你在實現你的服務的時候必須依賴其它ORB對象代理請求時,你就要當心了,你可能由於這樣而阻塞掉全部Communicator的Server端LF線程池,至使網絡Reactor癱瘓。若是你使用了bidirection connection來提供callback的飼服,要是你在callback的實現中依賴了其它ORB對象代理請求時,你一樣也要當心了,你可能由於這樣而阻塞掉全部Communicator的Client端LF線程池,至使網絡Reactor癱瘓。Ice爲了不,會默認爲每一個鏈接加上一個計時器,讓鏈接自動斷開。可是咱們仍是有應用場合但願鏈接長九不斷開,關閉這種機制,這時就要當心了。再者就是,即便你全部的ORB對象請求代理都用異步方式(ami,amd)進行編程,可是悲劇的是你沒法干涉到你依賴的其它人函數沒有進行同步阻塞的ORB對象代理請求。async

相關文章
相關標籤/搜索