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=text/plain' 頭,除非訊息本身已包含 'contentType' 頭。 換句話說,由頭提供的 contentType 優先於按繫結設定的 contentType。3. 現在,藉助 HandlerMethodArgumentResolvers 和預配置或使用者提供的 MessageConverters,傳入訊息將被轉換為應用程式提供的 MessageHandler 的簽名(例如,public Text process(Foo foo){..})。此類處理程式方法通常使用 @StreamListener@ServiceActivator@Transformer 等註解。在此過程中,某些轉換器可能需要 contentType,而步驟 2 中的操作可確保此類訊息始終透過其 contentType 頭可用。當然,如果此類方法以 Message 作為其輸入引數,則不會執行任何轉換。4. 呼叫處理程式方法,成功後,將開始從處理程式方法的返回值建立傳出訊息的過程(假定處理程式方法不是 void)。5. 處理程式方法返回的值將被轉換回 Spring Message,僅當返回值本身不是 Message 時才進行此操作。這意味著將建立一個新的 Spring Message,其載荷是處理程式的返回值。傳入訊息的頭將被複制到新的傳出訊息中,但會剝離任何由'SpringIntegrationProperties.messageHandlerNotPropagatedHeaders'標識的頭。預設情況下,只有一個頭設定在此處——contentType。這意味著新建立的傳出訊息沒有設定 contentType 頭。這是為了確保 contentType 可以隨著實際資料的應用程式級轉換而演變。注意:僅當處理程式方法返回非 Message 物件時,才會剝離 contentType 訊息將被髮送到繫結器的輸出通道。6. 與繫結器的輸入通道類似,繫結器的輸出通道(例如,Processor.OUTPUT)也預先配置了通道攔截器。在這裡,我們可以選擇性地將 contentType 頭注入傳出訊息,為將傳出訊息的內容轉換回線上傳輸格式做準備。我們來看僅有的兩種可能場景:a. *傳出 Message 有一個設定好的 contentType*。由於頭設定的 contentType 優先於任何其他 contentType,因此不會執行任何 contentType 注入,並且頭設定的 contentType 的值將在轉換回線上傳輸格式時使用。b. *傳出 Message 沒有設定 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 社群所有即將舉行的活動。

檢視所有