Spring Cloud Stream 2.0 - 內容型別協商與轉換

工程 | Oleg Zhurakousky | 2018 年 2 月 26 日 | ...

這是為 Spring Cloud Stream 2.0.0.RELEASE 做準備的一系列預釋出部落格中的第一篇。

前言

Spring Cloud Stream 2.0 對基於通道的繫結器的內容型別協商進行了徹底改造,以解決效能、靈活性以及最重要的一致性問題。以下部落格將重點介紹已完成的工作、預期結果以及它如何幫助您。

引言

資料轉換是任何訊息驅動微服務架構的核心功能之一。在 Spring Cloud Stream 中,此類資料表示為 Spring Message

在訊息流(即流)的各個點,訊息可能需要轉換為所需的形狀/大小才能到達其目的地。這有兩個原因

1. 將傳入訊息的內容線路格式轉換為匹配應用程式提供的處理器的簽名。 2. 將傳出訊息的內容轉換為下一個處理器(如果存在內部流程)的簽名,或轉換回線路格式。

線路格式通常是 byte[],並由繫結器實現控制。

在 Spring Cloud Stream Message 中,轉換透過 org.springframework.messaging.converter.MessageConverter 抽象完成。

以下步驟序列展示了典型的訊息流以及 Message 所經歷的轉換,使用 Spring Cloud Stream 的 Processor 契約進行描述,本質上涵蓋了入站出站內容轉換背後的要求。

1. 從繫結器接收線路格式的 Spring Message 2. 確保在 Spring Message 中設定了入站 contentType 頭部 3. 將 Spring Message 線路格式轉換為應用程式提供的 MessageHandler 的簽名 4. 呼叫應用程式提供的 MessageHandler 5. 將 MessageHandler 的返回值轉換回 Spring Message 6. 確保在 Spring Message 中設定了出站 contentType 頭部 7. 將 Spring Message 轉換回線路格式 8. 將線路格式的 Spring Message 傳送回繫結器

雖然上述內容完整總結了典型訊息流中的主要狀態變化,但魔鬼總是藏在細節中,所以讓我們更仔細地檢視每個步驟。

細節

  1. 傳入訊息由繫結器接收,並以線路格式傳送到繫結器的輸入通道(例如,Processor.INPUT')。
  2. 內部輸入通道預配置了通道攔截器,僅當傳入訊息尚未設定 contentType 頭部時,才為傳入訊息注入 contentType 頭部。這是必需的,以確保在需要時,下游訊息轉換可以考慮 contentType(稍後詳細介紹)。注入的 contentType 來自每個單獨目的地繫結的內容型別設定,其中 application/json 是預設內容型別。

例如,'spring.cloud.stream.bindings.myInput.content-type=text/plain' 為 'myInput'(傳入)目的地繫結設定內容型別為 'text/plain'。這意味著除非訊息已包含 'contentType' 頭部,否則每個傳入訊息都將注入 'contentType=text/plain' 頭部。 換句話說,頭部提供的 contentType 優先於每個繫結設定的 contentType。 3. 現在,藉助 HandlerMethodArgumentResolvers 和預配置或使用者提供的 MessageConverters,傳入訊息將轉換為應用程式提供的 MessageHandler 的簽名(例如,public Text process(Foo foo){..})。此類處理方法通常使用 @StreamListener@ServiceActivator@Transformer 等註解之一進行標註。在某些轉換器可能需要 contentType 的地方,步驟 2 中的操作保證了此類訊息始終可以透過其 contentType 頭部獲取它。當然,如果此類方法將其輸入引數設定為 Message,則不執行轉換。 4. 呼叫處理方法,並在成功後,從處理方法的返回值建立傳出訊息的過程開始(假設處理方法非 void)。 5. 僅當返回的值不是 Message 時,才將處理方法的返回值轉換回 Spring Message。這意味著會建立一個新的 Spring Message,其有效載荷是處理方法的返回值。傳入訊息的頭部會複製到新的傳出訊息中,同時剝離由 'SpringIntegrationProperties.messageHandlerNotPropagatedHeaders' 標識的任何頭部。預設情況下,這裡只設置了一個頭部 - contentType。這意味著建立的新傳出訊息未設定 contentType 頭部。這是為了確保 contentType 可以隨著實際資料的應用程式級轉換而演變。 注意:僅當處理方法返回非 Message 時,才剝離 contentType 訊息將傳送到繫結器的輸出通道。 6. 與繫結器的輸入通道類似,繫結器的輸出通道(例如,Processor.OUTPUT)也預配置了通道攔截器。在這裡,我們選擇性地將 contentType 頭部注入到傳出訊息中,為將傳出訊息的內容轉換回線路格式做準備。我們來看僅有的兩種可能情況: a. 傳出訊息已設定 contentType 頭部。由於頭部設定的 contentType 優先於任何其他 contentType,因此不會執行 contentType 注入,並且在轉換回線路格式時將使用頭部設定的 contentType 的值。 b. 傳出訊息未設定 contentType 頭部。繫結 contentType(預設或提供)將作為頭部注入到傳出訊息中,並在轉換回線路格式時使用。 7. 使用可用的 MessageConverters 之一將訊息轉換為線路格式。 8. 轉換後的訊息被髮送回繫結器,同時保留注入的或已有的 contentType 頭部。換句話說,傳出訊息將始終存在 contentType 頭部。

定製

上述內容涵蓋了預設的開箱即用行為。但這可能還不夠,所以我們能否以及如何進行定製?。2.0 中內容型別協商改進的目標不僅是回答這些型別的問題,還要確保答案是一致的——入站出站通道攔截器用於轉換為/從線路格式轉換的 'MessageConverters' 與 'HandlerMethodArgumentResolvers' 用於轉換為/從強型別轉換的 'MessageConverters' 是相同的。

要新增自定義的 MessageConverter,只需建立一個 org.springframework.messaging.converter.MessageConverter 的實現,並將其配置為 @Bean,同時將該 bean 註解為 @StreamMessageConverter,它將作為現有 MessageConverters 堆疊中的第一個轉換器新增,本質上優先於現有的 MessageConverters

總結

希望現在已經相當清楚,任何和所有內容型別轉換都是由 MessageConverters 完成的。雖然 MessageConverters 的實現方式不同,但大多數都同時利用 contentType 頭部和目標型別(targetClass),這使得它們可以執行型別內部轉換以及轉換為/從線路格式轉換。目前,有一套預配置的 MessageConverters 來支援大多數用例,因此對於大多數典型資料型別(即 json、文字等),終端使用者實際上無需做任何事情。然而,瞭解當前的工作方式以及如何定製是值得的——定製現有實現和/或引入新的 MessageConverter 實現

結論

我們目前正在更新文件,其中將包含有關此主題以及與 2.0 相關工作的更多詳細資訊和示例,而這些預釋出部落格的主要目標是提高認識、促進“試一試”並徵集反饋。話雖如此,Spring Cloud Stream 2.0.0.RC1 可在此處獲取。

我們鼓勵您使用以下任一方式提供反饋

盡情享受!

訂閱 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

搶佔先機

VMware 提供培訓和認證,助您加速發展。

瞭解更多

獲取支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群的所有即將舉行的活動。

檢視全部