從同步到非同步再到反應式方法,再到 Eclipse Microprofile 反應式訊息傳遞。
許多基於微服務的應用程式都是使用流行的 RESTful 方法在這些微服務之間進行通訊的,這些微服務通常被稱為命令式微服務。 然而,隨著響應式程式設計的日益普及,許多開發人員正在將他們的應用程式從以前使用的命令式邏輯改進為非同步、非阻塞的函式式風格。
但是,很難知道從哪裡開始進行這種轉換,並使其對應用程式的響應速度和響應速度更快。 本文將引導你完成從命令式到響應式微服務的旅程,並說明何時需要考慮編寫響應式微服務。
為了理解為什麼響應式方法在開發人員中越來越受歡迎,以及為什麼如此多的應用程式轉向響應式方法,讓我們看乙個簡單的演示應用程式。
演示應用程式由兩個微服務組成:Service-A 和 Service-B。 最初,它們通過 RESTful 呼叫連線在一起,這些呼叫向應用程式的客戶端公開 Service-A 終結點,如下圖所示。
就目前而言,沒有問題。 但是,有一天,service-b 停止響應,阻止了 service-a。 這會導致您的應用被阻塞且無響應。 要解決此問題,您可以將 Service-A 和 Service-B 之間的呼叫從同步更改為非同步,從而允許 Service-A 在等待 Service-B 重新聯機的同時執行其他任務。
使用 Jax-RS 和 MicroProfile 的非同步 REST(Open Liberty 部落格,2019 年 1 月):了解如何對 Jax-RS 進行非同步呼叫。
從同步呼叫更改為非同步呼叫輕而易舉。 您可以在 J**A 8 中使用 CompletionStages。 然而,一旦進入非同步世界,就會出現新的謎題。 例如,您現在需要管理 j**a ee 上下文。 執行緒池中的任何新執行緒都不會從其父執行緒繼承任何上下文。 這是乙個問題,因為安全上下文、JNDI(J**a 命名和目錄介面)和 CDI(上下文和依賴關係注入)通常需要與分配給方法呼叫的任何新執行緒相關聯。 那麼你是怎麼做到的呢? 幸運的是,Eclipse MicroProfile 也有答案:MicroProfile 上下文傳播。 Open Liberty 中的 Microprofile 上下文傳播(Open Liberty 部落格,2019 年 8 月):深入探討 Microprofile 上下文傳播以及如何從父執行緒捕獲上下文,然後將其與新執行緒相關聯。
Microprofile Context Propagation 引入了 ManageDexecutor 和 ThreadContext API,用於管理由執行緒池排程並由應用程式執行時管理的執行緒的上下文。 微配置檔案上下文傳播中的受管執行程式允許您使用完成階段,這些階段在可以使用的執行緒的上下文中執行,而不管操作最終在哪個執行緒上執行。 使用 Microprofile 上下文傳播時,執行緒上下文是完全確定的,因為上下文始終從建立完成階段的執行緒中捕獲,並在執行操作時應用。 以下清單顯示了使用 Microprofile 上下文傳播來傳播安全性和應用程式上下文的示例。
@qualifier@retention(retentionpolicy.runtime)@target()public @interface securityandappcontext {}produces @applicationscoped @securityandappcontextmanagedexecutor createexecutor( @configproperty(name="exec1.maxasync", defaultvalue="5") integer a, @configproperty(name="exec1.maxqueued", defaultvalue="20") integer q)
但是,只有在後端可靠的情況下,將應用程式轉換為僅使用非同步呼叫才有用。 如果後端不可靠,並且微服務頻繁失敗,非同步執行緒將變得無響應,保持掛起狀態,等待後端。 為了確保應用程式通訊的非同步性是有效的,需要提高所涉及的微服務的彈性。 為此,您可以利用 Microprofile Fault Tolerance。
Microprofile Fault Tolerance 提供以下功能來幫助確保微服務具有彈性:@retry
:處理臨時網路故障@circuitbreaker
:快速失敗,可重複故障@bulkhead
:防止單個微服務導致整個系統癱瘓@timeout
:為關鍵業務任務設定時間限制@fallback
:提供備份計畫。
以下清單演示了如何使用它@retry
跟@fallback
構建安全且可復原的微服務
@retry(maxretries = 2) @fallback (applyon=, skipon=exceptionbsub.class, fallbackmethod="fallbackforserviceb") publicstring serviceb() private string fallbackforserviceb()
使用 Microprofile Fault Tolerance 進行安全且有彈性的非同步程式設計:第 1 部分(Open Liberty 部落格,2020 年 6 月):了解有關 Fault Tolerance 如何幫助進行非同步呼叫的更多資訊。
許多開發人員認為,通過實現非同步程式設計,他們的應用程式現在是非阻塞的。 不幸的是,大多數情況都不是那麼簡單。 僅靠非同步程式設計並不能解決阻塞執行執行緒的問題。 如果應用程式中的微服務需要很長時間才能響應,則執行該程序的執行緒將被阻塞並等待響應。 被阻止的執行緒越多,應用程式的響應速度就越低。 嘗試解決此問題的一種方法是分配更多執行緒以能夠處理更多程序,但執行緒不是無限的。 當所有可用執行緒都用完時會發生什麼情況? 應用程式停止執行,並且不會以任何方式對使用者做出反應。
隨著開源的興起,許多應用程式還利用了第三方,應用程式開發人員可能不熟悉第三方,或者想知道這是否是非阻塞的。 這也可能導致申請流程中的潛在阻塞。
那麼,如何才能真正使我們的應用程式無塊呢? 為此,我們需要轉向被動方法。 如果您不確定“反應式”一詞的真正含義,請檢視文章定義“反應式”一詞(IBM Developer,2020 年 8 月)。 正如 Reactive Manifesto 中所述,響應式、安全和彈性應用程式由訊息驅動的非同步主幹網提供支援。 Microprofile Reactive Messaging 支援在應用程式元件之間進行基於訊息的非同步通訊,從而提供了一種建立響應式微服務的簡單方法。 它使微服務能夠非同步傳送、接收和處理作為連續事件流接收的訊息。 Miroprofile Reactive Messaging 還使用另外兩個規範並與之互操作:Reactive Streaming,用於背壓非同步流處理。 它定義了一組最小的介面,允許將執行此類流處理的元件連線在一起。 Microprofile Reactive Streams Operators,它提供了一組基於響應流的基本運算子,用於將不同的響應元件鏈結在一起並處理它們之間傳遞的資料。 借助 MicroProfile Reactive Messaging,您可以使用提供的@incoming
跟@outgoing
用於注釋應用程式 Bean 方法的注釋。 跟@incoming
注釋的方法將使用來自通道的訊息。 跟@outgoing
帶批註的方法將訊息發布到通道。 同時@incoming
跟@outgoing
帶注釋的方法是乙個訊息處理程式,它從通道獲取訊息,對訊息進行一些轉換,然後將訊息發布到另乙個通道。
以下**列表是乙個@incoming
注釋示例,其中my-channel
表示通道,併發送到my-channel
為每條訊息呼叫該方法。
@incoming("my-channel")public completionstage consume(message message)
以下**列表是乙個@outgoing
注釋示例,其中my-channel
是目標通道,並且為每個消費者請求呼叫該方法。
@outgoing("my-channel")public message publish()
可以使用org.eclipse.microprofile.reactive.messaging.message#of(t)
建立乙個簡單的組織eclipse.microprofile.reactive.messaging.message。
然後,這些帶注釋的方法被轉換為與反應式流相容的發布者、訂閱者和處理器,並使用通道將它們連線在一起。 通道是指示使用訊息的源或目標的名稱。 通道是不透明的字串。
下圖顯示了分配給方法 A、B 和 C 的批註@outgoing
跟@incoming
以及它們如何使用通道連線在一起(在這種情況下是順序和狀態)。
有兩種型別的通道:
內部通道是應用程式的原生通道。 它們支援多步驟處理,其中來自同一應用程式的多個 Bean 形成乙個處理鏈(如上圖所示)。 外部通道連線到遠端**或訊息傳遞層,例如 Apache Kafka。 外部通道由聯結器使用聯結器 API 進行管理。 聯結器充當擴充套件,用於管理與特定傳輸技術的通訊。 Microprofile Reactive Messaging 的大多數實現都將包括一些最流行和最常用的遠端裝置(如 Apache Kafka)的預配置聯結器。 但是,您也可以建立自己的聯結器,因為反應式訊息傳遞規範提供了實現聯結器的 SPI。 這樣一來,MicroProfile Reactive Messaging 就不會限制您使用的訊息**。 Open Liberty 支援基於 Kafka 的訊息傳遞。
將配置與微服務分離(Open Liberty 指南):了解有關微概要檔案配置及其功能的更多資訊。
通過應用程式配置將特定通道對映到遠端接收器或訊息源。 請注意,實現可能提供各種方法來配置對映,但必須支援微配置檔案配置作為配置源。 在 Open Liberty 中,可以在 MicroProfile Config 讀取的任何位置設定配置屬性。 例如,作為開放自由的bootstrap.properties
file 或 Open Libertyserver.env
檔案中的環境變數,以及自定義配置的其他源。 為了幫助展示微配置檔案反應式訊息傳遞的真實示例,我們建立了乙個小型演示應用程式,其中包含兩個微服務:reactive-service-a 和 reactive-service-b,它們使用 kafka 連線。 在此示例應用程式中,reactive-service-a 充當發布者,將訊息發布到通道initial-prices
。以下清單來自 reactive-service-aproducerbean.j**a
類,顯示@outgoing
請注意,將服務發布到的通道 (.)initial-prices
) 以及每種方法中的內容。
@applicationscopedpublic class producerbean);}
渠道initial-prices
它已經在microprofile-config.properties
檔案,如以下清單所示。 頻道對映到 Kafka 主題topic1
mp.messaging.outgoing.initial-prices.connector=liberty-kafkamp-messaging.outgoing.initial-prices.topic=topic1
reactive-service-b 是使用 reactive-service-a 生成的訊息的使用者。 在下面的**列表**中,您可以看到通行證@incoming
注釋使用prices
頻道的訊息。
@applicationscopedpublic class kafkaconsumer }
但請注意,在 reactive-service-b KafkaConsumer 中j**a 類,頻道名稱現在為prices
,而不是在 reactive-service-a 中使用initial-prices
。這是因為 reactive-service-bmicroprofile-config.properties
檔案,相同的 Kafka 主題 (topic1
) 現在對映到名為prices
的頻道。 這有助於演示如何鬆散地解耦這兩個微服務,使用完全不同的通道名稱對映到同乙個 Kafka 主題,因為它們只是不透明的字串。 這樣,如果不同的團隊設計、構建和管理兩個微服務,他們就不必使用相同的頻道名稱。
本文基於Emily 江和Clement Escoffier題為“響應式微服務在行動”的會議報告。 您可以展示會議中的幻燈片。 了解有關反應系統的更多資訊。 **免費電子書 Reactive Systems Explained (O'Reilly) 或檢視 Reactive Systems 入門(IBM Developer,2020 年 4 月)。 建立響應式 J**A 微服務(Open Liberty 指南):動手探索如何提高應用程式的響應能力。 本指南詳細介紹了如何使用 MicroProfile Reactive Messaging 建立反應式微服務。 開始使用企業應用程式支援的執行時構建響應式系統。 這裡有多種選項,包括來自 OpenLiberty 和 MicroProfile 的反應式 API,以及通過 Red Hat Runtimes 的 vertX 和 Quarkus,兩者都已合併到 IBM Cloud Pak for Applications 中。 使用 IBM Event Streams 構建事件驅動型應用程式,IBM Event Streams 是乙個基於開源 Apache Kafka 構建的完全受支援的事件流式處理平台,可簡化業務關鍵型工作負載的自動化。 借助 IBM Event Streams,組織可以快速部署企業級事件流技術。