對Netflix Ribbon的Loadbalancer類原始碼設計合理性的一點質疑
首先,這只是我個人的一點質疑,可能是因為我自己菜沒有領悟到作者的意思,也正因此,想發出來給大家一起探討.
在昨晚,我因為在編寫自己的開源專案的負載均衡模組(這是我開源專案的介紹:https://www.cnblogs.com/yangfeiORfeiyang/p/9621909.html),所以去看了下Netflix的RibbonLoadbalancer的原始碼,然後它是這樣做的
首先定義一個ILoadBalancer介面,這個介面是幹嘛的呢?大家可以想象為一個自動飲料機,我們想要飲料的時候,按一個按鍵(chooseServer(Object key)),投一個硬幣(當然它的幾個原始碼實現類裡,這個key其實沒有多少意義),就能獲得我們想要的飲料,由此來看,大家可以將它抽象為一個對我們隱藏了內部細節的容器
下面再來看一個介面,這個介面是幹什麼的呢?將註釋裡的英文翻譯下,就是這個介面是定義負載均衡的規則的,最典型的負載均衡規則就是輪詢啊,根據響應時間進行自動權重啊(根據響應時間這個,它的原始碼裡是有實現類的ResponseTimeWeightedRule不過被標記為了過時類,也就是不推薦我們使用)
好了,接下來就是我認為它不合理的地方了,請看下面這個實現了IRule的抽象類,請注意它的成員變數,它封裝了ILoadbalancer類
請大家想象一個場景,現在你想去一個超市買水果,你有可能走路去,有可能騎車去,也有可能做公交車,不管怎樣,去超市買水果這是一個沒有細節的概念,為什麼沒有細節呢?因為我們不知道你會以什麼樣的方式去,此時你會以什麼樣的方式去就是一個可能會發生變化的變數.
那麼對比到我們的程式碼裡,ILoadBalancer就是你要去超市買水果,這是一個沒有變化的,封閉了細節的概念,試問你在自動飲料機買飲料的時候知道飲料機內部經歷了什麼樣的變化嗎?而IRule就是我們是以什麼樣的方式去超市了,此時它是可能會發生變化的,因為我們可能用輪詢演算法,有可能用權重演算法,但不管用什麼,它們最終都會取出一個Server.,這個結果是不會改變的,也就代表
我們的ILoadBalancer是不會改變的,那麼你將ILoadBalancer封裝到IRule裡是什麼意思呢?我"個人"認為,正確的做法應該是這樣的:
好吧,我繼續看了一會又發現一個問題,這個問題也在驗證了我的想法是對的
很明顯,作者自己也知道,IRule應該是ILoadbalancer的細節
what???敢情您饒了一大圈最後又繞回去了呀.我們再看看rule.choose(key)裡的具體內容
額額額,你調來調去最後又調回來了...
不過,有一行程式碼讓我感覺到了作者的高併發程式設計水平真的不錯...
首先這個nextServerCyclicCounter是個AtomticInteger類.
我們先說說為什麼作者不直接nextServerCyclicCounter.getAndIncrement(),然後取模呢?因為如果要getAndIncrement的話,那麼會導致一個問題,那麼就是我就要寫出這樣的程式碼
if(nextServerCyclicCounter.get()==服務數量){
nextServerCyclicCounter.set(0)
}
但是這樣就有一個問題,那就是雖然AtomticInteger類的每個操作都是原子性的,但是加起來就不是了,也就是說,假設有兩個執行緒,一個A,一個B.
第一步:執行緒A剛剛進入了判斷,還沒來得及設定為0,CPU時間片就被搶了
第二步:執行緒B也進來了,那麼他們拿到的將是同樣的資料.連續兩次設定為0.當然可能會有N個執行緒進來N次設定為0
而用這個方法呢?就不會有這個問題了,它等於是拿到當前值,然後再拿預期值,之後,看看當前值是否是預期值,不是的話就從新獲取,是的話就返回預期值,沒錯,這裡最妙的就是這個死迴圈包含的程式碼,它等於是每次重新拿最新的值去試(因為AtomticInteger類裡的value欄位上了volatile關鍵字),完美的避開了我們上述的問題.
正當我感嘆這個作者實在是高的時候...我看了下譯文
額,好吧,我也算是受到了您的啟發,學到了一手,哈哈哈
完畢~歡迎大家說出我不對的地方...