2011-11-15 65 views
5

我在netty中實現了一個服務器,該服務器處理用戶請求,與中間件通話併發送響應。與往返中間件相比,I/O預計可以忽略不計,所以爲了最大限度地減少阻塞,我在OrderedMemoryAwareThreadPoolExecutor之上的管道中有一個ExecutionHandler。目前沒有問題。優先處理ThreadPoolExecutors中的Netty任務

我正在研究服務器在重負載下的行爲。根據過去的協議經驗,我們往往會被偶然的DOS攻擊所淹沒 - 通常是用戶的腳本陷入無限循環或類似情況。理想情況下,我們可以在他們的頻道超過某個使用率閾值後對頻道進行優先排序,以便其他用戶的服務不受影響。

我已經實現了一個簡單的ThreadPoolExecutor,它使用PriorityBlockingQueue並基於從我們自己的Session類(連接到ChannelHandler中的上下文)中提取的數據設置優先級。再次,到目前爲止沒有問題。

嘗試利用netty的內置ThreadPoolExecutors的排序和內存感知的困難。理想情況下,MyThreadPoolExecutor可以只擴展OrderedMemoryAwareThreadPoolExecutor並在優先級隊列中連線。唉這是不可能的,原因有兩個:私人和最終。更詳細:

一)ThreadPoolExecutor.workQueue可以在構造函數中設置,但MemoryAwareThreadPoolExecutor硬編碼這是一個LinkedTransferQueue,並且不公開這個到其子OrderedMemoryAwareThreadPoolExecutor(即MyThreadPoolExecutor不能訪問設置它)。如果有必要,這可以通過基於反射的私人領域調整的醜陋克服。

b)我想能夠重寫MyThreadPoolExecutor.doUnorderedExecute(),這樣我就可以插入優先級處理並構造必要的對象,但它被聲明爲final。調用它的代碼不需要改變。

結果是,爲了保持所有漂亮的netty功能,但使用優先級隊列,我不得不復制'n'paste兩個OrderedMemoryAwareThreadPoolExecutor和MemoryAwareThreadPoolExecutor,調整每一行的幾行,然後從那裏延伸。這不會讓我成爲好的編程習慣!即使考慮到它引發了警鐘。

現在的幾個問題:

1)我解決了錯誤的問題?我爲了什麼我想要完全吠叫完全錯誤的樹?

2)如果不是,還有比上面討論的更好的方法嗎?

3)上述方法帶來了總服務器負載一直處於容量狀態的非優先級任務的飢餓風險。我已經準備好容忍'淘氣'的用戶,但是一旦它們恢復到正常狀態,它們現有的任務仍然會捱餓,並且爲了保持順序,任何新的,更高優先級的任務都必須添加到它們後面。你有關於如何最好地處理這個問題的建議嗎? (禁止用戶不被業務所允許。)

4)這是半問題,半反饋。 OrderedMemoryAwareThreadPoolExecutor的netty文檔爲線程X提供了一個方便的圖表Y - 大概這些是ThreadPoolExecutor中彙集的線程而不是I/O工作線程?這可能值得更加清楚。另外,當不使用ExecutionHandler時,每個通道都綁定到單個I/O工作線程 - 當執行一個ExecutionHandler時,這仍然是這種情況嗎?即是否將任務添加到ExecutionHandler的順序保證與它們​​到達Channel的順序相同?如果是這樣的話,那麼我看不出MemoryAwareThreadPoolExecutor的文檔中的線程X如何在事件1之前處理事件2 - 我接受在這裏不同的線程可以按任何順序完成工作,但我看不到工作是如何進行的(它從workQueue中彈出)。 ExecutionHandler中的文檔暗示了這一點,但會從更多的細節中受益。

非常感謝您的閱讀,任何幫助都非常感謝。

回答

0

您的'警鐘'解決方案看起來很熟悉 - 只需從一個小池大小開始。

你似乎在說你需要一個慢速服務器的解決方案。 你可能想看看爲什麼你的服務器在負載下變慢。 - 線程爭用(比賽)問題。 - 初始池大小 - GC配置

+2

該服務器仍在開發中,但它正在替換的(C++)數據庫受到強烈的數據綁定。我們通過有限的工作線程解決了高負載降低數據庫(v。壞消息)的可能性,但是這又承認了一個飢餓的用戶可能扼殺所有工作人員並惡化其他人的服務(我們有保持非常緊密的SLA)。我們在舊服務器上解決這個問題的方法是基於netty不支持的假設,所以我們必須提出更好的東西 - 因此是一個問題。我們不擔心遊泳池的大小呢! – Liche

3

1)不,你的想法很好。這只是OrderedMemoryAwareThreadPoolExecutor缺乏這樣的功能。你會file an issue

2)我只是叉OrderedMemoryAwareThreadPoolExecutor,簡化它,並添加優先級隊列。這樣,您就可以更好地控制隊列項目(事件)的處理方式。

3)可以使用兩個隊列 - 一個用於高優先級項目,另一個用於低優先級項目,而不是使用優先級隊列。該線程可以首先處理高優先級隊列,但是您可以控制該循環,以防止循環時間過長。

4)是的,它們是來自ThreadPoolExecutor的線程。如果不清楚,我們應該更新我們的文檔。請隨時提出問題或直接貢獻它。

+0

謝謝,我會對這些變化加以解決,如果我獲得僱主的許可,我會在適當的時候將其提交給項目。 – Liche

+0

我建議比兩個隊列或PriorityQueue更優雅的解決方案。 OMATPE的實現使得一個Channel的任務隊列通過主workQueue被賦予一個Worker,該Worker在返回並獲得另一個頻道的工作之前執行所有該頻道的任務(以及在平均時間內添加的任務)。因此,繁忙的渠道可以壟斷一名工人。因此,對於低優先級的通道,處理最多固定數量的任務,然後將其推回到workQueue的尾部。因此,它在沒有飢餓的情況下進行處理,但是要限制它可以佔用多少資源。 (儘管額外的開銷。) – Liche