脈絡:應用程式有一項服務想開放給其他應用程式使用。

為什麼不想被迫二選一#

應用程式未必想決定某項服務(Service Layer [EAA] 中的一個操作)該同步還是非同步被喚起,它可能想對同一項服務同時支援兩種作法

而且:

設計要與其他應用程式協作的應用程式(例如 B2B 應用)時,開發者可能不知道自己要與哪些應用程式溝通、通訊又會如何進行訊息技術與資料格式太多了,不可能為了以防萬一就全部支援。

接收與處理一則訊息牽涉數個步驟,要把這些步驟分開既困難又不必要地複雜;然而,把「接收訊息、取出內容、依內容執行工作」混在一起的 Message Endpoint 程式碼,會很難重用。

設計支援多種通訊風格的客戶端時,很容易覺得必須為每種風格各自重新實作那項服務——這讓支援每種新風格都很麻煩,還帶來「各風格的行為未必完全一致」的風險

我們需要的,是讓單一項服務支援多種通訊風格的方式。

解法#

  • Service Activator 可以是**單向(僅請求)雙向(Request-Reply)**的
  • 服務可以簡單到只是一次方法呼叫——同步且非遠端,可能是 Service Layer [EAA] 的一部分
  • 啟動器可以寫死成永遠喚起同一項服務,也可以用反射喚起訊息所指明的服務

運作流程#

  1. Service Activator 接收請求訊息(可以是 Polling Consumer,也可以是 Event-Driven Consumer
  2. 它知道訊息的格式,處理訊息以取出「該喚起哪項服務」與「該傳入哪些參數值」所需的資訊
  3. 像服務的任何其他客戶端一樣喚起服務,並在服務執行期間阻塞
  4. 服務完成並回傳值時,啟動器可選擇性地建立一則含有該值的回覆訊息並回傳給請求方(此時這次服務喚起就成了 Request-Reply 訊息的實例)

錯誤處理#

  • 若 Service Activator 無法成功處理訊息,那則訊息就是無效的,應該被移到 Invalid Message Channel
  • 若訊息能被處理、服務也被成功喚起,那麼執行服務過程中發生的任何錯誤都是應用程式的語意錯誤,應由應用程式處理

開發者仍然無法預測夥伴可能想用什麼方式存取自己的服務,但他們至少知道自己的應用程式會提供哪些服務、並能把它們實作出來——之後依需要為不同技術與格式實作新的啟動器就相對容易了。

延伸:與相關模式的關係

Service Activator 模式也被記載於 [CoreJ2EE](模式名最初出於該書)。

那個版本與本書版本略有不同——它假設啟動器是 Event-Driven Consumer,也假設服務已經存在、啟動器是被加到服務上的——但兩個版本以非常相似的方式,為同一個問題提出了相同的解法。

Service Activator 也與 Half-Sync/Half-Async 模式 [POSA2] 相關,後者把服務處理分成同步與非同步兩層。

搭配關係:

  • Service Activator 通常接收 Command Message,因為它描述了該喚起哪項服務
  • Service Activator 本身扮演 Messaging Gateway,把訊息細節與服務分離
  • 啟動器可以是 Polling ConsumerEvent-Driven Consumer
  • 若服務具交易性,啟動器就該是 Transactional Client,讓訊息消費能與服務喚起參與同一個交易
  • 多個啟動器可以是 Competing Consumers,或由 Message Dispatcher 協調
  • 無法成功處理訊息時,應把訊息送往 Invalid Message Channel
範例:J2EE Enterprise JavaBeans

以 J2EE 中的 EJB 為例:

  1. 把服務封裝成 session bean
  2. 為各種訊息情境實作 message-driven bean——一個對應「使用某種格式訊息的 JMS 目的地」、另一個對應「使用另一種格式的另一個目的地」、再一個對應 web service/SOAP 訊息,依此類推

想同步喚起服務的客戶端,可以直接存取那個 session bean。 >