2012-02-23 54 views
5

我有以下情況:NServiceBus - 如何爲每個消息類型接收器訂閱的單獨隊列?

enter image description here

因此,接收器訂閱兩種事件:eventA和eventB。 NServiceBus爲接收方(Receiver)創建隊列,並將類型爲eventA和eventB的消息放置到同一隊列中。問題是,如果我可以配置NServiceBus爲接收方的每種類型的事件使用單獨的隊列(ReceiverEventA和ReceiverEventB)?或者我可以在單個進程中有兩個接收器(並且每個接收器都有獨立的隊列)。 問題是,EventA比EventB處理時間要長得多,而且它們是獨立的 - 所以如果它們處於單獨的隊列中,它們可以同時處理。

更新:如果我有這樣幼稚的做法去,接收器無法啓動與空引用異常:

private static IBus GetBus<THandler, TEvent>() 
    {     
     var bus = Configure.With(new List<Type> 
            { 
             typeof(THandler), 
             typeof(TEvent), 
             typeof(CompletionMessage) 
            }) 
      .Log4Net() 
      .DefaultBuilder() 
      .XmlSerializer() 
      .MsmqTransport() 
      .IsTransactional(true) 
      .PurgeOnStartup(false) 
      .UnicastBus() 
      .LoadMessageHandlers() 
      .ImpersonateSender(false); 

     bus.Configurer.ConfigureProperty<MsmqTransport>(x => x.InputQueue, "Queue" + typeof(THandler).Name); 

     return bus.CreateBus().Start(); 
    } 

    [STAThread] 
    static void Main() 
    { 
     Busses = new List<IBus> 
        { 
         GetBus<ItemEventHandlerA, ItemEventA>(), 
         GetBus<ItemEventHandlerB, ItemEventB>() 
        };   

     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 
     Application.Run(new TestForm()); 
    } 

異常堆棧跟蹤是:

在NServiceBusTest2.WinFormsReceiver.Program在C:\ Users \ User \ Documents \ Visual Studio 2010 \ Projects \ NServiceBusTest2 \ NServiceBusTest2.WinFormsReceiver \ Program.cs中的.GetBusTHandler,TEvent中:NServiceBusTest2.WinFormsReceiver.Program.Main()中的第57行
。 User \ Documents \ Visual Studio 2 010 \項目\ NServiceBusTest2 \ NServiceBusTest2.WinFormsReceiver \的Program.cs:行26
在System.AppDomain._nExecuteAssembly(RuntimeAssembly組件,字串[] args)
在System.AppDomain.ExecuteAssembly(字符串assemblyFile,證據assemblySecurity,字符串[ ]參數) 在Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
在System.Threading.ThreadHelper.ThreadStart_Context(對象狀態)
在System.Threading.ExecutionContext.Run(的ExecutionContext的ExecutionContext,ContextCallback回調,對象的狀態, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback callback,Object state)
在System.Threading.ThreadHelper.ThreadStart()

回答

5

我開始寫一個更清晰的解釋,爲什麼你不應該有兩個單獨的過程,以支持我對@stephenl發佈的答案的評論。 NServiceBus基本上爲每個進程強制執行一個輸入隊列。

所以通常情況下,你會有兩個獨立的進程。 EventAService會從QForEventA中讀取EventA,在與EventBService分開的進程中讀取來自QForEventB的EventB。

然後,我更仔細地查看了您的示例代碼,並意識到您處於Windows窗體應用程序中。咄!現在我覺得有點愚蠢。當然,你只能有一個過程。想象一下,如果在啓動Outlook之後,您還必須啓動MailService.exe才能真正獲得郵件!

所以問題是,您的Windows窗體應用程序中的EventA和EventB的處理時間截然不同。我沒有辦法知道這個工作是什麼,但這對客戶端應用程序來說有點奇怪。

大多數情況下,這是一項需要大量處理的服務,並且客戶端收到的任何消息都相當輕量級 - 「實體X已更改,因此下次需要直接加載它從數據庫中「並處理它只涉及從緩存中刪除某些東西 - 當然不是一個長期運行的過程。

但聽起來好像無論出於何種原因在客戶端的處理需要更長的時間,這在WinForms應用程序是由於對UI線程編組的擔憂有關,阻塞UI線程等

我建議系統在WinForms應用程序中,您不需要在NServiceBus處理程序中執行所有處理,但可以將其從其他位置編組。把它作爲工作項目扔到ThreadPool中,或者類似的東西。或者將長時間運行的項目放入隊列中,並以自己的速度對這些項目進行後臺線程緊縮。那樣,NServiceBus消息處理程序所做的就是「是的,收到了消息,非常感謝。」那麼,一次處理一個NServiceBus消息應該無關緊要。

更新:

在評論中,OP詢問你扔的工作線程池NServiceBus finsihes收到後會發生什麼。當然,這種方法的另一面 - 在NServiceBus完成之後,你是獨立的 - 如果它在ThreadPool中失敗了,那麼由你來創建你自己的重試邏輯,或者只是捕獲異常,提醒WinForms應用程序的用戶,並讓它死亡。

很明顯,這是最優的,但它引發了一個問題 - 只是在WinForms應用程序中完成了哪些工作?如果NServiceBus提供的健壯性(有害消息的自動重試和錯誤隊列)是這個難題的關鍵部分,那麼爲什麼它首先在WinForms應用程序中進行呢?這個邏輯可能需要被卸載到WinForms應用程序外部的服務中,在這個服務中,每個消息類型(通過部署單獨的服務)具有單獨的隊列變得很容易,然後只有影響UI的部分纔會被髮送回WinForms客戶端。當到用戶界面的消息僅影響用戶界面時,處理它們幾乎總是微不足道的,而且您不需要卸載到ThreadPool來跟上。

直接針對您在GitHub Issue中描述的情況,這聽起來像是每種消息類型的單獨進程完全是規定解決方案的場景。我聽說部署和管理這麼多進程似乎聽起來不可思議,但我認爲你會發現它並不像聽起來那麼糟糕。甚至有優勢 - 如果你必須用亞馬遜重新部署你的連接器。例如,你只需要重新部署這個端點,不需要任何其他的停機時間,或者擔心可能會引入其他錯誤。

爲了簡化部署,希望您使用持續集成服務器,然後簽入幫助部署腳本的工具,如DropkicK。就我個人而言,我最喜歡的部署工具是Robocopy。甚至像1)NET STOP ServiceName,2)ROBOCOPY,3)NET START ServiceName相當有效。

+0

我正在考慮將ThreadPool作爲工作項目拋出,但如果它失敗了呢?處理程序將完成它的工作,所以消息將從隊列中移除,並且沒有標準方法將其返回(或者我錯過了什麼?)。我們使用Windows窗體作爲流程管理工具 - 我在這裏提到了更具體的示例:https://github.com/NServiceBus/NServiceBus/issues/219 – Giedrius 2012-03-19 07:19:15

+1

@Giedrius:提供了答案正文中的更新。 – 2012-03-19 15:04:26

+0

感謝您的幫助。要回答你的問題 - WinForms是讓儀表板暫停/恢復,同時查看銷售渠道的當前狀態 - 這是處理的內容。我們發現有一種方法可以讓多個接收器使用不同的AppDomain,但總的來說,這是一種痛苦的解決方法。看起來我們必須接受AppDomain的解決方法,或者搜索NServiceBus的替代方案,或者重新考慮部署(現在我們有cc.net + clickOnce),並將儀表板和處理程序設置爲單獨的部分。 – Giedrius 2012-03-19 15:19:51

2

你不需要,你只需要創建一個處理程序,在接收器中的每個事件。例如,

public class EventAHandler : IHandleMessages<EventA>{} 

public class EventBHandler : IHandleMessages<EventB>{} 

如果要分隔隊列,則需要將每個處理程序放入單獨的端點。那有意義嗎?

另外我覺得你的圖需要一點工作。我確信它是一種標籤,但我看到它的方式是Host是實際的發佈者,而不是客戶端端點(您已將其命名爲發佈者A和發佈者B)。

UPDATE:它的一個簡單的例子,但說明了我是什麼意思 - https://github.com/sliedig/sof9411638

我已經擴展附帶NServiceBus全雙工樣品包括三個額外的端點,其中兩個訂閱通過被髮布的事件服務器分別和一個處理兩者的端點。 HTH。

+0

關於命名,這只是示例,所以可能你是對的,那些更像發件人,主機是發佈者。關於隊列,如果兩個事件都被推送到同一個隊列,是不是說如果EventA先到達並且需要很長時間來處理它,那麼EventB將等待直到EventA被處理?我認爲如果他們在單獨的隊列中,他們可以得到一致處理,或者我是我錯了? – Giedrius 2012-02-23 14:44:15

+1

如果您將其配置爲使用多個線程,那麼您可能不會有太多問題阻止對方。話雖如此,你可能仍然會有大量的EventAs,它們會捆綁所有的線程並阻止EventB的處理。如果這是您非常關心的問題,則應該爲EventB提供單獨的接收端點。 – 2012-02-23 17:34:30

+0

那麼,配置它使用多線程需要商業許可證,據我所知:)而我們沒有那麼多的消息,購買商業許可證,只是EventA和EventB的平均處理時間是非常不同的。你能指點我有兩個接收端點的接收器嗎? – Giedrius 2012-02-24 07:39:08

相關問題