2013-05-20 79 views
3

我正在構建一個利用ZeroMQ N到N pub/sub模型的POC。從我們的應用服務器,當一個http請求被服務時,如果線程從數據庫中提取數據,它會用這些數據更新一個本地memcache實例。要應用服務器集羣中同步其他內存緩存的情況下,請求線程發送與使用ZMQ發行該數據的消息......所以問題是:什麼策略方面是最有效的減少插座創建/ destory開銷時應用程序有很多線程依賴於套接字發送消息?我們是否共享套接字池,我們是否創建/銷燬每個線程的套接字等?ZeroMQ多線程:按需創建套接字或使用套接字對象池?

策略1 - 線程管理髮布的Socket
在這種方法中,每個線程,T1T2,並T3,通過創建它,建立連接,發送管理Socket對象(發行人)的生命週期消息,最後關閉套接字。基於this,這當然是最安全的方法,但是當套接字重複創建,連接和銷燬時,我們擔心開銷問題;如果開銷對性能產生負面影響,我們希望避免它。

enter image description here

策略2 - 出版商套接字對象池
在這種方法中,父進程(應用服務器)初始化ZMQ出版商在啓動池。當一個線程需要一個發佈者時,它從對象池中獲取一個,發送它的消息,然後將發佈者返回到池;創建,連接和銷燬插座的過程中被消除相對於使用發佈線程,但訪問池同步以避免任何兩個線程使用相同的出版商對象在同一時間,而這正是死鎖和併發問題可能會出現。

我們還沒有成型的兩種方法,因爲想要做一個試金石上,因此,測試第一。就批量而言,我們的應用程序不會發布「繁重」,但可能需要發佈消息的同時需要100-150個線程(每個應用程序服務器)。

ZMQ Publisher Object Pool

因此,要重申:什麼策略是相對於同時強調性能當應用程序有依賴於出版商發送消息的線程開銷最小化的最有效

+0

線程不能重用自己的專用套接字嗎? – flup

+0

不,這些是HTTP處理程序線程,由應用程序服務器管理;我會更新問題,thx。 – raffian

+0

什麼是編程語言/應用服務器? – flup

回答

2

你真的不能問一個關於性能的問題,而不爲您估計的吞吐量提供實時數據。我們在談論每秒10個請求,100,1000,10K嗎?

如果HTTP服務器真的爲每個請求創建和銷燬線程,那麼重複創建0MQ套接字將會強調操作系統,並且取決於請求量和進程限制,它會工作,否則它會用完的手柄。你可以測試這個微不足道的,這是第一步。

然後,(通過「ZMQ出版商」你的意思)共享一個套接字池是骯髒的。人們這樣做,但套接字是而不是線程安全,所以它意味着當您將套接字切換到另一個線程時非常小心。

如果有辦法讓線程持續那麼每個人都可以創建其PUB插座是否需要,只要它存在抓住它。如果不是這樣,那麼我的第一個設計就是無論如何會創建/銷燬套接字,但是使用inproc://將消息發送到單個永久轉發線程(SUB-PUB代理)。我會測試這個,然後如果它打破了,去尋找更具異國情調的設計。

通常,最簡單的設計和打破它,而不是過度考慮設計過程(特別是在開始時)。

+0

Pieter,哪個例子最好地說明了'inproc'和永久性轉發器線程的用法? (感謝您的信息!) – raffian

+0

與'clientThread.connect(tcp:/// ...)'相比,您建議使用'clientThread.connect(inproc:// ...)'是因爲較少的開銷如果客戶反覆創建和銷燬發佈商,那麼'inproc'?如果是這樣的話,我現在明白了你在指南中所說的關於使用線程之間的消息進行通信而不是MT和鎖的說法。 – raffian

1

對我來說,這聽起來像是過早的優化,如果可能的話,你應該堅持第一個策略,讓自己免於頭疼。

但作爲第二種選擇的替代方案,您可以在應用程序內部維護一個Executor線程池來執行實際的zmq發送。這樣每個執行程序線程都可以保留自己的套接字。您可以監聽應用程序/ servlet生命週期事件,以瞭解何時關閉池並清理套接字。

編輯:

要做到這一點最簡單的方法是使用Executors.newFixedThreadPool()創建執行人,並給它使用一個ThreadLocal插座可運行工作。 (請參閱Java Executors and per-thread (not per-work unit) objects?)線程將只創建一次,並從此開始重用,直到執行程序關閉。

如果在作業的run()方法中引發異常,這會有點棘手。我懷疑你會發現你需要對執行程序線程的生命週期進行更多的控制。如果是這樣,你可以複製源newFixedThreadPool

return new ThreadPoolExecutor(nThreads, nThreads, 
           0L, TimeUnit.MILLISECONDS, 
           new LinkedBlockingQueue<Runnable>()); 

和子類化被實例化來定製它的ThreadPoolExecutor。通過這種方式,您可以重寫afterExecute來檢測並清理損壞的套接字。

發送作業通過阻塞隊列傳遞給工作線程。我意識到,這不是ZeroMQ方式將消息傳遞給工作線程,這將是不成熟的消息。這將ZeroMQ從HTTP生命週期不受控制的HTTP工作線程移開,因此難以維護套接字,更接近應用程序的邊緣。你必須簡單地測試哪一個更有效率,並且需要判斷你的應用程序有多嚴格地採用ZeroMQ消息傳遞範例進行線程間通信。

+0

我在查看Executor模型,但是我發現所有示例都是按需創建和執行worker,我想創建這些worker(publishers)並將它們添加到工作池中,然後將請求發送到工作隊列以執行實際發送。你有這樣的例子嗎? – raffian

+1

我已經添加了一些創建和使用固定線程池的指針 – flup

+0

我同意,基於Pieter的評論,'inproc'可能是最好的方法,但如果它不符合預期,我也會嘗試這種方法,謝謝。 – raffian