乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Java 和微服務系列第 4 部分 處理數(shù)據

       xujin3 2018-08-18

      在微服務系統(tǒng)中,具有混合持久性的環(huán)境時,有必要讓數(shù)據處理保持可管理。為了解釋如何實現(xiàn)此目標,本章會簡要介紹微服務在數(shù)據處理方面的特征,然后介紹如何使用基于 Java 的微服務實現(xiàn)此目的。

      微服務特定于數(shù)據的特征

      識別數(shù)據的一種方式是自上而下的方法,數(shù)據必須存儲在您的微服務的數(shù)據存儲中。從業(yè)務級別開始建模您的數(shù)據。以下各節(jié)將介紹如何識別此數(shù)據,如何處理它,以及如何與其他微服務的其他數(shù)據存儲共享它。有關自上而下方法的描述,請訪問網站。

      領域驅動設計導致產生各種實體

      如果采用領域驅動設計中的方法,您會獲得以下對象(和其他對象):

      • 實體

      '一種不通過屬性定義,而通過連續(xù)性和身份定義的對象。'

      • 價值對象

      '一種包含屬性,但沒有概念實體的對象。它們應視為不可變。'

      • 聚合

      '一種通過根實體(也稱為聚合根)綁定到一起的對象集合。聚合根通過禁止外部對象持有其成員的引用,確保在聚合內執(zhí)行的更改的一致性。'

      • 存儲庫

      '檢索領域對象的方法應委托給專門的存儲庫對象,以便能夠輕松地交換備選的存儲實現(xiàn)。'

      這些引語出自來源:點此網址。

      這些對象應映射到您的持久存儲(僅在實體具有相同的生命周期時才能聚合它們)。這種領域模型是邏輯數(shù)據模型和物理數(shù)據模型的基礎。

      有關領域驅動設計術語的更多信息,請訪問這個網站。

      每個微服務都有單獨的數(shù)據存儲

      每個微服務都應擁有自己的數(shù)據存儲(如圖 1),而且與其他微服務及其數(shù)據分離。第二個微服務不能直接訪問第一個微服務的數(shù)據存儲。

      采用此特征的原因是:

      • 如果兩個微服務共享一個數(shù)據存儲,它們將緊密耦合。如果更改一個微服務的數(shù)據存儲(比如表)的結構,則可能導致另一個微服務出現(xiàn)問題。如果微服務以這種方式耦合,則必須協(xié)調新版本的部署,而這是我們必須避免的。

      • 每個微服務都應該采用最能滿足其需求的數(shù)據庫類型(混合持久性,請參閱小節(jié)'混合持久性'了解更多的細節(jié))。在選擇數(shù)據庫系統(tǒng)時,不需要在不同微服務之間進行權衡。這種配置會為每個微服務提供一個單獨的數(shù)據存儲。

      • 從性能角度講,每個微服務都有自己的數(shù)據存儲可能很有用,因為擴展會變得更容易。數(shù)據存儲可托管在自己的服務器上。

      圖 1 每個微服務都有單獨的數(shù)據存儲

      對于關系數(shù)據庫,可通過以下方式之一實現(xiàn)數(shù)據存儲的分離:

      • 每個微服務一種模式

      每個服務在數(shù)據庫中都有自己的模式(如圖 2)。其他服務可使用同一個數(shù)據庫,但必須采用不同的模式??赏ㄟ^數(shù)據庫訪問機制實現(xiàn)這種配置(為連接的數(shù)據庫用戶使用權限),因為面臨時間壓力的開發(fā)人員傾向于使用快捷方式,而且能直接訪問其他模式。

      • 每個微服務一個數(shù)據庫

      每個微服務都可以有自己的數(shù)據庫,但與其他微服務共享數(shù)據庫服務器(如圖 2)。使用不同的數(shù)據庫,用戶可以連接到數(shù)據庫服務器,而且實現(xiàn)了很好的數(shù)據庫分離。

      圖 2 每個微服務一種模式和每個微服務一個數(shù)據庫

      • 每個微服務一個數(shù)據庫服務器

      這是最高程度的分離。舉例而言,在需要解決性能方面的問題時,這可能很有用(如圖 3)。

      圖 3 每個微服務一個數(shù)據庫服務器

      混合持久性

      每個微服務應使用自己的數(shù)據存儲,這意味著該服務也可以采用不同的數(shù)據存儲技術。NoSQL 運動催生了許多新的數(shù)據存儲技術,它們可與傳統(tǒng)關系數(shù)據庫一起使用。基于應用程序必須滿足的需求,可選擇不同類型的技術來存儲數(shù)據。一個應用程序中擁有各種各樣的數(shù)據存儲技術,這被稱為混合持久性。

      對于一些微服務,最好將其數(shù)據存儲在關系數(shù)據庫中。對于其他具有不同類型的數(shù)據(非結構化、復雜和面向圖形的數(shù)據)的服務,可將它們的數(shù)據存儲在一些 NoSQL 數(shù)據庫中。

      多語言編程這個詞的含義是,應用程序應該使用最適合它必須解決的挑戰(zhàn)的編程語言來編寫。

      Martin Fowler 網站提供了這兩個術語的更詳細的描述。

      跨微服務的數(shù)據共享

      在一些情況下,微服務應用程序的用戶可能希望請求不同的服務所擁有的數(shù)據。例如,用戶可能想查看他的所有支付和支付的相應狀態(tài)。因此,用戶需要在服務中查詢他的帳戶,隨后在服務中查詢他的支付情況。微服務會使用數(shù)據庫合并數(shù)據,不是一種最佳實踐。要處理此業(yè)務案例,您必須實現(xiàn)一個適配器服務(如圖 4),使用它查詢帳戶服務和支付服務,然后將收集到的數(shù)據返回給用戶。適配器服務還負責對它收集到的數(shù)據執(zhí)行必要的數(shù)據轉換。

      圖 4 適配器微服務

      更改數(shù)據可能變得更復雜。在微服務系統(tǒng)中,一些業(yè)務事務可能涵蓋多個微服務。舉例而言,在零售店的微服務應用程序中,可能有一個下單服務和一個支付服務。

      因此,如果客戶想在您的商店購買商品并付款,那么業(yè)務事務需要包含兩個微服務。每個微服務都有自己的數(shù)據存儲,所以對于包含兩個或更多微服務的業(yè)務事務,涉及到兩個或更多個數(shù)據存儲。本節(jié)將介紹如何實現(xiàn)這些業(yè)務事務。

      事件驅動架構

      您需要一種方法來確保包含兩個或多個微服務的業(yè)務事務中涉及的數(shù)據的一致性。一種方法是采用分布式事務,但眾多原因表明,不應在微服務應用程序中采用這種方法。主要原因是,分布式事務中涉及的微服務是緊密耦合的。如果一個分布式事務涉及兩個微服務,而且一個服務失敗或出現(xiàn)性能問題,那么另一個服務必須等到超時后回滾事務。

      讓一個業(yè)務事務涵蓋多個微服務的最佳方式是,使用事件驅動架構。要更改數(shù)據,第一個服務負責更新它的數(shù)據,并在同一個(內部)事務中發(fā)布一個事件。第二個微服務(它已訂閱此事件)收到此事件,并對它的數(shù)據執(zhí)行更改。通過使用發(fā)布/訂閱通信模型,兩個微服務是松散耦合的。僅在它們交換的消息上存在耦合。該技術使微服務系統(tǒng)能在所有微服務之間保持數(shù)據一致性,而無需使用分布式事務。

      如果微服務應用程序中的微服務相互發(fā)送許多消息,那么它們可能非常適合合并到一個服務中。但是需要謹慎一些,因為此配置可能破壞微服務的領域驅動設計。執(zhí)行復雜更新(涵蓋多個服務)的適配器服務也可以使用事件實現(xiàn)。

      事件驅動架構的編程模型更為復雜,但 Java 有助于讓該復雜性處于可管理狀態(tài)下。

      最終一致性

      在事件驅動架構中,將消息發(fā)送給其他微服務會導致所謂的'最終一致性'問題。這通常是以下情況下導致的運行時問題:微服務 A 更改其數(shù)據存儲中的數(shù)據,并在同一個內部事務中向微服務 B 發(fā)送一條消息。經過較短時間后,微服務 B 收到消息,并更改其數(shù)據存儲中的數(shù)據。在這個通常很短的時間段內,兩個數(shù)據存儲中的數(shù)據不一致。例如:服務 A 更新其數(shù)據存儲中的訂單數(shù)據,并向服務 B 發(fā)送一條消息以進行支付。在處理支付之前,有一個未支付的訂單。當消息接收者無法處理消息時,情況就會變遭。在這種情況下,消息系統(tǒng)或接收微服務必須采取一些策略來處理此問題。

      在微服務應用程序中,每個微服務擁有自己的數(shù)據庫。一次涵蓋多個微服務的業(yè)務事務會導致'最終一致性'問題,因為分布式事務會阻礙此問題的解決。處理這種業(yè)務事務的一種方法如圖 5 所示。訂單微服務將訂單保存到它的數(shù)據存儲中,并將一個事件(例如 OrderCreated)發(fā)送給支付微服務。在訂單微服務未從支付微服務收到支付確認期間,訂單處于未支付狀態(tài)。

      支付服務訂閱了 OrderCreated 事件,所以它會處理該事件,并在其數(shù)據存儲中執(zhí)行支付。如果支付成功,它會發(fā)布一個被訂單微服務訂閱的 PaymentApproved 事件。處理 PaymentApproved 事件后,訂單狀態(tài)從'未支付'更改為'已批準'。如果客戶查詢其訂單狀態(tài),就會獲得以下兩種響應之一:訂單未支付或訂單已批準。

      圖 5 微服務之間的事件消息

      在數(shù)據不可用的情況下,服務可能向客戶發(fā)送類似下面這樣的消息:'很抱歉,請稍后重試'。

      數(shù)據復制

      數(shù)據存儲與從不同數(shù)據存儲獲取數(shù)據的需求分離,可能導致使用數(shù)據庫系統(tǒng)的數(shù)據復制機制可以解決問題的想法。舉例而言,跟與多個微服務共享數(shù)據庫相比,使用數(shù)據庫觸發(fā)器、計時存儲過程或其他流程來解決此問題同樣存在缺點。更改復制管道的一端的數(shù)據結構會給復制流程帶來問題。在部署服務的新版本時,必須調整該流程。這也是一種緊密耦合形式,必須避免。

      前面已經提到,基于事件的處理可將兩個數(shù)據存儲分離。如果需要的話,處理事件的服務可執(zhí)行適當?shù)臄?shù)據轉換,將數(shù)據存儲在它們自己的數(shù)據存儲中。

      事件尋源和命令查詢職責分離

      在事件驅動架構中,可以考慮命令查詢職責分離 (CQRS) 和事件尋源。可結合這兩種架構模式來處理流經您的微服務應用程序的事件。

      CQRS 將對數(shù)據存儲的訪問拆分為兩個不同部分:一部分包含讀取操作,另一部分包含寫入操作。讀取操作不會更改系統(tǒng)的狀態(tài)。它們僅返回狀態(tài)。寫入操作(命令)會更改系統(tǒng)的狀態(tài),但不會返回值。事件尋源存儲在數(shù)據發(fā)生更改時發(fā)生的事件序列。圖 6 顯示了一個使用 CQRS 的示例。

      圖 6 使用 CQRS 的示例

      如圖 6 所示,事件按順序存儲在事件存儲中。查詢模型中的數(shù)據與來自事件存儲的數(shù)據同步。要支持事件存儲或查詢模型,可以使用專門的系統(tǒng)(例如 Elastic Search)來支持微服務的查詢。

      此架構也可用于處理微服務應用程序中的事件。

      消息系統(tǒng)

      可以使用消息系統(tǒng)或面向消息的中間件來支持事件驅動架構。

      '面向消息的中間件 (MOM) 是一種軟件或硬件基礎架構,用于支持在分布式系統(tǒng)之間發(fā)送和接收消息。MOM 允許應用程序模塊分布在異構的平臺上,這降低了開發(fā)涵蓋多個操作系統(tǒng)和網絡協(xié)議的應用程序的復雜性。中間件創(chuàng)建了一個分布式通信層,這一層將應用程序開發(fā)人員與各種操作系統(tǒng)和網絡接口的細節(jié)隔離??缍鄻踊钠脚_和網絡進行擴展的 API 通常由 MOM 提供。MOM 提供的軟件元素位于客戶端/服務器架構的所有通信組件中,通常支持客戶端與服務器應用程序之間的異步調用。MOM 減少了客戶端/服務器機制的主從性質的復雜性與應用程序開發(fā)人員的關聯(lián)。'

      要與面向消息的中間件進行通信,可以采用不同的協(xié)議。以下是最常用的協(xié)議:

      • 高級消息隊列協(xié)議 (AMQP)

      AMQP'規(guī)定消息提供者和客戶端的行為,以便使來自不同供應商的實現(xiàn)可互操作,就像 SMTP、HTTP、FTP 等創(chuàng)建可互操作的系統(tǒng)一樣。'

      • MQ 遙測傳輸 (MQTT)

      MQTT 是'基于發(fā)布-訂閱的輕量型'消息協(xié)議,用在 TCP/IP 協(xié)議之上。它專為與需要'小代碼體積'或網絡帶寬有限的遠程位置建立連接而設計。'它主要用在物聯(lián)網 (IoT) 環(huán)境中。

      在 Java 世界中,有一個 API 可與面向消息的中間件通信:

      Java Message Service (JMS),它包含在 Java EE 規(guī)范中。具體的版本是 JMS 2.0。

      由于 JMS 的悠久歷史(可追溯到 2001 年),有許多 JMS 消息代理可用作 MOM 系統(tǒng)。不過也有一些實現(xiàn) AMQP 的消息系統(tǒng):

      – RabbitMQ

      – Apache Qpid

      – Red Hat Enterprise MRG

      所有這些系統(tǒng)都提供了 Java API,所以它們可用在基于 Java 的微服務中。

      分布式事務

      大部分(不是所有)消息系統(tǒng)都支持事務。在將消息發(fā)送給消息系統(tǒng)和更改事務數(shù)據存儲中的數(shù)據時,也可以使用分布式事務。

      可在微服務與它的后備存儲之間使用分布式事務和兩階段提交,但不要在微服務之間使用它們??紤]到微服務的獨立性,特定服務實例之間不得存在關聯(lián),而兩階段提交事務需要這種關聯(lián)。

      對于涵蓋多個服務的交互,必須添加補救和調解邏輯來保證一致性。

      Java 中的支持

      在混合持久性環(huán)境中,用于實現(xiàn)微服務的編程語言必須處理不同的持久性技術。您的編程語言必須能支持每種持久保存數(shù)據的不同方式。作為編程語言,Java 擁有許多 API 和框架,可幫助開發(fā)人員處理不同的持久性技術。

      Java Persistence API

      'Java Persistence API (JPA) 是一種在 Java 對象/類與關系數(shù)據庫之間訪問、持久化和管理數(shù)據的 Java 規(guī)范。EJB 3.0 規(guī)范中將 JPA 定義為取代 EJB 2 CMP Entity Beans 規(guī)范的一種規(guī)范。現(xiàn)在,在 Java 行業(yè)中,JPA 被視為對象關系映射 (ORM) 的標準行業(yè)方法。

      JPA 本身只是一個規(guī)范,不是產品;它本身無法無法執(zhí)行持久化或任何其他操作。JPA 只是一組接口,需要一種實現(xiàn)。有開源和商用的 JPA 實現(xiàn)可供選擇,而且所有 Java EE 5 應用服務器都應支持使用它。JPA 還需要一個數(shù)據庫來實現(xiàn)持久化。'

      Java Enterprise Edition 7 (Java EE 7) 包含 Java Persistence 2.1 (JSR 338)。

      發(fā)明 JPA 主要是為了得到一種對象關系映射器,以便在關系數(shù)據庫中持久保存 Java 對象。API 背后的實現(xiàn)(持久化提供程序)可由不同的開源項目或供應商實現(xiàn)。使用 JPA 的另一個好處是,您的持久化邏輯更容易移植。

      JPA 定義了自己的查詢語言(Java 持久化查詢語言 (JPQL)),可為不同數(shù)據庫供應商生成查詢。Java 類被映射到數(shù)據庫中的表,也可以使用類之間的關系來建立對應的表之間的關系??墒褂眠@些關系建立級聯(lián)操作,所以一個類上的操作可能導致其他類的數(shù)據上的操作。JPA 2.0 引入了 Criteria API,可幫助在運行時和編譯時獲得正確的查詢結果。在應用程序中實現(xiàn)的所有查詢都有一個名稱,可以通過這個名稱找到它們。這種配置使得在完成編程幾個星期后更容易知道查詢的功能。

      JPA 持久化提供程序實現(xiàn)數(shù)據庫訪問。以下是最常用的提供程序:

      • Hibernate

      • EclipseLink

      • Apache OpenJPA

      Liberty for Java EE 7 的默認 JPA 提供程序是 EclipseLink。

      JPA 概述

      下面的簡要介紹和代碼段展示了 JPA 的特性。開始使用 JPA 的最佳方式是創(chuàng)建實體類來持有數(shù)據(示例 1)。

      示例 1 持有實體數(shù)據的 JPA 類

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      @Entity
      public class Order {
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      private Long id;
      private String description;
      @Temporal(TemporalType.DATE)
      private Date orderDate;
      public String getDescription() {
      return description;
      }
      public void setDescription(String description) {
      this.description = description;
      }
      public Date getOrderDate() {
      return orderDate;
      }
      public void setOrderDate(Date orderDate) {
      this.orderDate = orderDate;
      }
      ……
      }

      每個實體類都需要一個 @Entity 注釋,然后才能通過持久化提供程序管理。實體類根據名稱映射到數(shù)據庫中的對應表(約定優(yōu)于配置)。也可應用不同的映射。類的屬性根據名稱映射到基礎表的列。也可覆蓋屬性的自動映射 (@Column)。每個實體類必須有一個實體(參見第2 部分'領域驅動設計'中的'將領域元素映射到服務')。持久化提供程序必須使用一個或多個身份列(使用 @Id 標注)來將對象的值映射到表中的數(shù)據行。數(shù)據庫或持久化提供程序可通過不同方式生成身份列的值。實體類的一些屬性必須以特殊方式轉換,才能存儲在數(shù)據庫中。例如,數(shù)據庫列 DATE 必須使用注釋 @Temporal 映射到實體類中。

      用于查詢數(shù)據庫的主要 JPA 接口是 EntityManager。它包含從數(shù)據庫創(chuàng)建、讀取、更新和刪除數(shù)據的方法??赏ㄟ^不同方式獲取 EntityManager 的引用,具體取決于應用程序運行的環(huán)境。在非托管環(huán)境中(沒有 servlet、EJB 或 CDI 容器),必須使用類的工廠方法 EntityManagerFactory,如示例 2 所示。

      示例 2 如何在 Java SE 環(huán)境中獲取 EntityManager

      1
      2
      3
      4
      EntityManagerFactory entityManagerFactory =
      Persistence.createEntityManagerFactory('OrderDB');
      EntityManager entityManager =
      entityManagerFactory.createEntityManager();

      字符串 OrderDB 是為其創(chuàng)建 EntityManager 的持久化單元的名稱。持久化單元用于對實體類和相關屬性進行邏輯分組,以配置持久化提供程序(persistence.xml 文件中的配置)。

      在托管環(huán)境中,情況更簡單。可從容器注入 EntityManager,如示例 3 所示。

      示例 3 如何在 Java EE 環(huán)境中獲取 EntityManager

      1
      2
      @PersistenceContext
      EntityManager em;

      如果注入持久化上下文時未使用 unitName,也就是配置文件 (persistence.xml) 中配置的持久化單元的名稱,則會使用默認值。如果只有一個持久化單元,那么該值就是 JPA 使用的默認值。

      以下各節(jié)將介紹如何使用來自 EntityManager 的方法實現(xiàn)一些簡單的創(chuàng)建、檢索、更新和刪除方法,如示例 4 所示。

      示例 4 JPA 創(chuàng)建操作

      1
      2
      3
      4
      5
      6
      7
      @PersistenceContext
      EntityManager em;
      ...
      public Order createOrder(Order order) {
      em.persist(order);
      return order;
      }

      EntityManager 的 persist 方法將執(zhí)行兩個操作。首先,EntityManager 管理該對象,這意味著它在其持久化上下文中保存該對象??蓪⒊志没舷挛囊暈橐环N緩存,在其中保存與數(shù)據庫中的行相關的對象。這種關系是使用數(shù)據庫事務來建立的。其次,該對象被持久存儲在數(shù)據庫中。如果 Order 實體類有一個 ID 屬性,該屬性的值由數(shù)據庫生成,那么該屬性的值將由 EntityManager 在將對象插入數(shù)據庫中后設置。正因如此,createOrder 方法的返回值為對象本身(示例 5)。

      示例 5 使用 find 方法的 JPA 讀取操作

      1
      2
      3
      4
      5
      6
      7
      @PersistenceContext
      EntityManager em;
      ...
      public Order readOrder(Long orderID) {
      Order order = em.find(Order.class, orderID);
      return order;
      }

      EntityManager 方法 find 在表中搜索以參數(shù)形式 (orderID) 提供主鍵的行。結果被轉換為一個 Order 類型的 Java 類(示例 6)。

      示例 6 使用 JPQL 的 JPA 讀取操作

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      @PersistenceContext
      EntityManager em;
      ...
      public Order readOrder(Long orderID) {
      TypedQueryOrder> query =
      em.createQuery( 'Select o from Order o ' +
      'where o.id = :id', Order.class );
      query.setParameter('id', orderID);
      Order order = query.getSingleResult();
      return order;
      }

      示例 6 通過使用 JPQL 和一個參數(shù)顯示了 find 方法的功能。從 JPQL 字符串 Select o from Order o where o.id = :id 開始,生成一個 TypedQuery。有了 TypedQuery,您就可以在生成結果對象后省略 Java 轉換(參見參數(shù) Order.class)。JPQL 中的參數(shù)可按名稱 (id) 進行查找,該名稱使得開發(fā)人員很容易理解它。方法 getSingleResult 確保僅在數(shù)據庫中找到一行。如果有多個行與 SQL 查詢對應,則拋出一個 RuntimeException。

      merge 方法在數(shù)據庫中執(zhí)行更新(示例 7)。參數(shù) order 是一個游離對象,這意味著它不在持久化上下文中。在數(shù)據庫中更新后,EntityManger 返回一個附加的(現(xiàn)在包含在持久化上下文中)order 對象。

      示例 7 JPA 更新操作

      1
      2
      3
      4
      public Order updateOrder(Order order, String newDesc) {
      order.setDescription(newDesc);
      return em.merge(order);
      }

      要刪除數(shù)據庫中的一行數(shù)據,需要一個附加對象(示例 8)。要將該對象附加到持久化上下文中,可以運行 find 方法。如果該對象已附加,則不需要運行 find 方法。通過使用 remove 方法和附加對象的參數(shù),可以在數(shù)據庫中刪除該對象。

      示例 8 JPA 刪除操作

      1
      2
      3
      4
      public void removeOrder(Long orderId) {
      Order order = em.find(Order.class, orderId);
      em.remove(order);
      }

      前面已經提到,必須使用某種配置來告訴持久化提供程序,在何處查找數(shù)據庫和如何處理數(shù)據庫。這在名為 persistence.xml 的配置文件中完成。該配置文件需要位于您的服務的類路徑中。

      依賴于您的環(huán)境(是否是 Java 容器),必須通過兩種方式之一完成配置(示例 9)。

      示例 9 Java SE 環(huán)境中的 Persistence.xml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      persistence>
      persistence-unit name='OrderDB'
      transaction-type='RESOURCE_LOCAL'>
      class>com.service.Orderclass>
      properties>
      property name='javax.persistence.jdbc.url'
      value='<> />
      property name='javax.persistence.jdbc.user'
      value='user1' />
      property name='javax.persistence.jdbc.password'
      value='password1' />
      property name='javax.persistence.jdbc.driver'
      value='.DriverClass>' />
      properties>
      persistence-unit>
      persistence>

      要配置持久化提供程序,必須做的第一件事就是定義持久化單元 (OrderDB)。持久化單元的一個屬性是 transaction-type??稍O置兩個值:RESOURCE_LOCAL 和 JTA。第一個選項讓開發(fā)人員負責在其代碼中執(zhí)行事務處理。如果您的環(huán)境中沒有事務管理器,那么可以使用該選項。第二個選項是 JTA,它表示 Java Transaction API,包含在 Java Community Process (JCP) 中。此選項告訴持久化提供程序,將事務處理委托給運行時環(huán)境中存在的事務管理器。

      在 XML 標記 之間,可以列出要在這個持久化單元中使用的實體類。

      在該文件的 properties 部分,可以設置值來配置持久化提供程序將處理數(shù)據庫的方式。以 javax.persistence.jdbc 開頭的屬性名稱由 JPA 標準定義。示例 9 展示了如何設置數(shù)據庫 URL(用于建立數(shù)據庫連接)、用戶名和密碼。javax.persistence.jdbc.driver 屬性告訴持久化提供程序應該使用哪個 JDBC 驅動程序類。

      Java EE 環(huán)境的配置文件中的主要區(qū)別如示例 10 所示。

      示例 10 Java EE 環(huán)境中的 Persistence.xml

      1
      2
      3
      4
      5
      6
      7
      persistence>
      persistence-unit name='OrderDB'>
      jta-data-source>jdbc/OrderDBjta-data-source>
      class>com.widgets.Orderclass>
      ...
      persistence-unit>
      persistence>

      JPA 中的默認事務處理方法是使用 JTA,所以您不需要在配置文件中設置它。jta-data-source 屬性指向 Java EE 環(huán)境的應用服務器中配置的數(shù)據源的 JNDI-Name。

      要在非 Java EE 環(huán)境中執(zhí)行事務管理,可使用 EntityManager 的一些方法,如示例 5-11 所示。

      示例 11 非 Java EE 環(huán)境中的事務管理

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      EntityManagerFactory emf =
      Persistence.createEntityManagerFactory('OrderDB');
      EntityManager em = emf.createEntityManager();
      EntityTransaction tx = em.getTransaction();
      tx.begin();
      try {
      em.persist(yourEntity);
      em.merge(anotherEntity);
      tx.commit();
      } finally {
      if (tx.isActive()) {
      tx.rollback();
      }
      }

      示例 11 給出了非 Java EE 環(huán)境中的事務處理過程。請避免自行在 Java EE 環(huán)境中執(zhí)行事務管理。還有更好的管理方式,如第小節(jié)'Enterprise JavaBeans'所述。

      要分離微服務的數(shù)據存儲,可以使用示例 12 中的配置文件來設置關系數(shù)據庫的默認模式。

      示例 12 在 my-orm.xml 中設置默認模式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <>xml version='1.0' encoding='UTF-8'?>
      entity-mappings xmlns='http://java./xml/ns/persistence/orm'
      xmlns:xsi='http://www./2001/XMLSchema-instance'
      xsi:schemaLocation='http://java./xml/ns/persistence/orm orm_2_0.xsd'
      version='2.0'>
      persistence-unit-metadata>
      persistence-unit-defaults>
      schema>ORDERschema>
      persistence-unit-defaults>
      persistence-unit-metadata>
      entity-mappings>

      必須在 JPA 配置文件 persistence.xml 中引用此文件(示例 13)。

      示例 13 persistence.xml 中引用一個映射文件的代碼段

      1
      2
      persistence-unit name='OrderDB'>
      mapping-file>custom-orm.xmlmapping-file>

      此配置將模式名稱被設置為所有 JPA 類的 my-orm.xml 映射文件中提供的名稱,在本例中為 OrderDB。它可確保您僅使用此模式中的表。

      結合使用 JPA 與 NoSQL 數(shù)據庫

      EclipseLink 是開始支持 NoSQL 數(shù)據庫的 JPA 提供程序之一(從 2.4 版開始)。從此版本開始,它們開始支持 MongoDB 和 Oracle NoSQL。預計在未來的版本中還會支持其他 NoSQL 數(shù)據庫。

      MongoDB 是一個面向文檔的 NoSQL 數(shù)據庫。它的數(shù)據結構有利于具有動態(tài)模式的 JSON 式文檔。MongoDB 擁有一個專門的 JSON 格式版本,名為 BSON。

      EclipseLink 2.5 版的EclipseLink 解決方案指南給出了一個訪問 MongoDB 的示例。

      在決定使用 JPA(像 EclipseLink 一樣)作為 MongoDB 的提供程序之前,請考慮以下幾點:

      1. SQL 是一種經過多次修訂的特定語言。數(shù)據庫供應商已實現(xiàn)此標準,但他們還向 SQL 添加了一些未標準化的特性。JPA 提供了對 SQL 的良好支持,但不是 MongoDB 公開的所有特性都受到支持。如果微服務只需要其中某些特性,那么您使用 JPA 所獲得的好處將會更少。

      2. JPA 有許多在面向對象的數(shù)據庫中沒有意義的特性,但 EntityManager 擁有處理這些特性的方法。所以您必須定義要在服務中使用哪些方法。

      如果您熟悉 JPA,而且只需要使用一些簡單功能將數(shù)據存儲在 NoSQL 數(shù)據庫中,那么可以開始使用 JPA 提供程序實現(xiàn)此目的。如果數(shù)據訪問變得更加復雜,那么最好使用來自 NoSQL 數(shù)據庫的 Java 驅動程序。JPA 并不真的適合 NoSQL 數(shù)據庫,但它是您的實現(xiàn)的不錯起點。有關如何使用 MongoDB 的原生 Java 驅動程序的示例,請訪問:

      http://docs./getting-started/java/

      要更充分地利用 JPA 提供程序,使用 Spring Data JPA 可能很有幫助。除了 JPA 提供程序之外,Spring Data JPA 還在 JPA 提供程序之上添加了一個額外層:

      http://projects./spring-data-jpa/

      JPA 對微服務中的數(shù)據處理的適合性

      下面列出了為什么 JPA 對微服務中的數(shù)據處理很有用的一些理由:

      • 從領域驅動設計角度定義微服務,這使得大部分微服務只需簡單查詢即可持久化其實體(簡單的創(chuàng)建、檢索、更新和刪除操作)。JPA 的 EntityManager 包含您所需的創(chuàng)建、檢索、更新和刪除方法:persist、find、merge、delete。要調用這些方法,無需完成太多編程工作。

      • 在一些情況下,查詢變得更為復雜。這些查詢可使用 JPA 中定義的查詢語言 JPQL 來完成。對復雜查詢的需求應是一種例外情況。具有實體數(shù)據分層結構的 JSON 文檔應分開存儲。這使得 ID 和查詢變得很簡單。

      • JPA 已經過標準化,擁有一個優(yōu)秀的社區(qū)來為您的微服務開發(fā)提供支持。所有 Java EE 服務器都必須支持 JPA。

      • 要實現(xiàn)不在 Java EE 容器中運行的微服務,也可以使用 JPA。

      • 從數(shù)據庫生成實體類(逆向工程),可減少必須自行編寫的代碼行數(shù)。

      • 對于混合持久性,JPA 支持關系數(shù)據存儲和面向文檔的數(shù)據存儲 (EclipseLink)。

      • JPA 是關系數(shù)據庫的一種抽象,允許您在需要時交換您的關系數(shù)據存儲與另一個關系數(shù)據存儲。這種可移植性消除了微服務的供應商鎖定。

      • 為了實現(xiàn)該策略,每個微服務都應在關系數(shù)據庫中擁有自己的模式,您可以在 JPA 配置文件 persistence.xml 中為您的服務設置默認模式。

      Enterprise JavaBeans

      Enterprise JavaBeans 3.2 (EJB)(在 JSR 345 中指定)包含在 Java EE 規(guī)范中。EJB 并不是普通的 Java 類,原因如下:

      • 它們擁有生命周期。

      • 它們由一個 EJB 容器(EJB 的運行時環(huán)境)管理。

      • 它們擁有更多很有幫助的特性。

      EJB 是服務器端軟件組件。從 EJB 3.0 開始,它不再使用部署描述符。EJB 的所有聲明都可在 EJB 類自身中使用注釋完成。與 CDI 管理 bean 的實現(xiàn)一樣,在 EJB 容器內處理 EJB 的實現(xiàn)也是輕量級的。EJB 的線程處理由 EJB 容器完成(類似于 servlet 容器中的線程處理)。EJB 的一個附加特性是,它們可與 Java EE Security 結合使用。

      EJB 可分為以下類型:

      • 無狀態(tài)

      • 有狀態(tài)

      • Singleton

      • 消息驅動 bean (MDB)

      無狀態(tài) EJB 無法擁有任何狀態(tài),但有狀態(tài) EJB 可以。由于微服務的特征,微服務中不應使用有狀態(tài) EJB。一個 Singleton Bean 僅在一個 Java EE 服務器中存在一次。異步消息處理中會結合使用 MDB 和 JMS 提供程序。

      EJB 可實現(xiàn)多個業(yè)務視圖,必須相應地注釋這些視圖:

      • 本地接口 (@Local)

      此接口中的方法只能由同一個 Java 虛擬機 (JVM) 中的客戶端調用。

      • 遠程接口 (@Remote)

      此接口中列出的方法可由 JVM 外部的客戶端調用。

      • 無接口 (@LocalBean)

      與本地接口大體相同,EJB 類的所有公共方法都向客戶端公開。

      在輕量型架構中(微服務應擁有這種架構),將 EJB 實現(xiàn)為無接口 EJB 會很有用。

      EJB 提供的主要好處之一是自動事務處理。每次調用一個業(yè)務方法時,都會調用 EJB 容器的事務管理器(例外:顯式關閉了事務支持的 EJB)。所以,很容易將 EJB 與事務數(shù)據存儲結合使用。將 EJB 與 JPA 相集成也很容易。

      示例 14 中的代碼段給出了一個將 EJB 與 JPA 框架結合的示例。

      示例 14 帶 PersistenceContext 的無狀態(tài)(無接口)EJB

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      @Stateless
      @LocalBean
      public class OrderEJB {
      @PersistenceContext
      private EntityManager entityManager;
      public void addOrder(Order order) {
      entityManager.persist(order);
      }
      public void deleteOrder(Order order) {
      entityManager.remove(order);
      }
      ...
      }

      根據小節(jié)'Java Persistence API'中的介紹,注入 EntityManager。

      EJB 可擁有以下事務屬性之一來處理事務數(shù)據存儲(必須由 EJB 容器實現(xiàn)):

      REQUIRED(默認)

      MANDATORY

      NEVER

      NOT_SUPPORTED

      REQUIRES_NEW

      SUPPORTS

      有關這些屬性的更多信息,請訪問網站。

      這些所謂的容器管理事務 (CMT) 可用在 EJB 的任何業(yè)務方法上。應避免 Bean 管理事務 (BMT),它們也可用在 EJB 中??稍陬惣墑e上設置注釋 TransactionAttribute,使該類的每個業(yè)務方法都有自己的事務屬性(示例 15)。如果不執(zhí)行任何設置,那么所有方法都將擁有默認事務級別 (REQUIRED)。方法級別的事務屬性會覆蓋類屬性。

      示例 15 在 EJB 中顯式設置事務屬性

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      @TransactionAttribute(REQUIRED)
      @Stateless
      @LocalBean
      public class OrderEJB {
      ...
      @TransactionAttribute(REQUIRES_NEW)
      public void methodA() {...}
      @TransactionAttribute(REQUIRED)
      public void methodB() {...}
      }

      未實現(xiàn)為 EJB 的 REST 端點

      在事務管理其決定提交時,會執(zhí)行一些數(shù)據庫更改或驗證。在某些情況下,驗證數(shù)據庫中的檢查約束是提交事務之前的最后一步。如果驗證失敗,JPA 提供程序將拋出一個 RuntimeException,因為 JPA 使用運行時異常來報告錯誤。如果使用 EJB 執(zhí)行事故管理,則捕獲 RuntimeException 的位置位于 EJB 的存根代碼中,EJB 容器將在這里執(zhí)行事故管理。存根代碼由 EJB 容器生成。因此,您無法處理此 RuntimeException,該異常被進一步拋出到它會被捕獲到的地方。

      如果將 REST 端點實現(xiàn)為 EJB,就像一些人喜歡的那樣,則必須在 REST 提供程序中捕獲該異常。REST 提供程序擁有異常映射器,可將異常轉換為 HTTP 錯誤代碼。但是,當在數(shù)據庫提交期間拋出 RuntimeException 時,這些異常映射器不會進行干預。因此,REST 客戶端會收到 RuntimeException,應該避免這種情況。

      處理這些問題的最佳方式是,將 REST 端點實現(xiàn)為 CDI 管理的請求范圍的 bean。在這個 CDI bean 中,可以使用與 EJB 內相同的注入方式。所以很容易將 EJB 注入 CDI 管理的 bean 中(示例 16)。

      示例 16 實現(xiàn)為 CDI 管理的 bean 且注入 EJB 的 REST 端點

      1
      2
      3
      4
      5
      6
      7
      @RequestScoped
      @Path('/Order')
      public class OrderREST {
      @EJB
      private OrderEJB orderEJB;
      ...
      }

      也可以將 EJB 與 Spring 集成(Enterprise JavaBeans (EJB) 集成 - Spring),而且如果您愿意的話,可以使用 Transaction Management Spring 執(zhí)行事務管理。但是,在 Java EE 領域,將事務管理委托給服務器會更好一些。

      有關 Spring 的更多信息,請訪問這個網站。

      BeanValidation

      BeanValidation 也包含在 Java EE 7 規(guī)范中:Bean Validation 1.1 JSR 349。Bean Validation 的用途是在 bean 數(shù)據上輕松地定義和執(zhí)行驗證約束。在 Java EE 環(huán)境中,Bean 驗證由不同的 Java EE 容器自動完成。開發(fā)人員只需要在屬性、方法或類上以注釋形式設置約束條件。驗證會在調用這些元素時自動完成(如果已配置)。驗證也可以在源代碼中顯式完成。有關 BeanValidation 的更多信息,請訪問網站。

      javax.validation.constraints 包中的內置約束示例如示例 17 所示。

      示例 17 BeanValidation 中的默認內置約束條件

      1
      2
      3
      4
      5
      private String username; // username must not be null
      @Pattern(regexp='\\(\\d{3}\\)\\d{3}-\\d{4}')
      private String phoneNumber; // phoneNumber must match the regular expression
      @Size(min=2, max=40)
      String briefMessage; // briefMessage netween 2 and 40 characters

      也可以組合使用多個約束條件,如示例 18 所示。

      示例 18 約束條件的組合

      1
      2
      3
      @NotNull
      @Size(min=1, max=16)
      private String firstname;

      還可以擴展約束條件(自定義約束條件)。示例 19 展示了如何自行執(zhí)行驗證。

      示例 19 以編程方式進行驗證

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      Order order = new Order( null, 'This is a description', null );
      ValidatorFactory factory =
      Validation.buildDefaultValidatorFactory();
      Validator validator = factory.getValidator();
      SetConstraintViolation> constraintViolations = validator.validate(order);
      assertEquals( 2, constraintViolations.size() );
      assertEquals( 'Id may not be null',
      constraintViolations.iterator().next().getMessage() );
      assertEquals( 'Order date may not be null',
      constraintViolations.iterator().next().getMessage() );

      可以通過配置 JPA 來自動執(zhí)行 Bean 驗證。JPA 規(guī)范要求,持久化提供程序必須驗證所謂的托管類(參見示例 20)。從 JPA 的意義上講,托管類是實體類。用于 JPA 編程的所有其他類也必須驗證(例如嵌入式類、超類)。此過程必須在這些托管類參與的生命周期事件中完成。

      示例20 開啟了 Bean 驗證的 Persistence.xml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      persistence>
      persistence-unit name='OrderDB'>
      provider>
      org.eclipse.persistence.jpa.PersistenceProvider
      provider>
      class> ...class>
      properties>
      property name='javax.persistence.validation.mode'
      value='AUTO' />
      properties>
      persistence-unit>
      persistence>

      使用的來自 Java EE 產品棧的所有其他框架都可用于自動驗證 bean(例如 JAX-RS、CDI),也可以使用 EJB,如示例 21 所示。

      示例 21 EJB 中的 Bean 驗證

      1
      2
      3
      4
      5
      6
      7
      @Stateless
      @LocalBean
      public class OrderEJB {
      public String setDescription(@Max(80) String newDescription){
      ...
      }
      }

      JPA 和 BeanValidation 的架構分層方面

      據微服務中實現(xiàn)的層,需要解決一些方面的問題。

      在僅包含少量層的服務中,使用 JPA 實體類作為數(shù)據傳輸對象 (DTO) 也更容易。將 JPA 對象與它的持久化上下文分離后,可以將它用作簡單 Java 對象 (POJO)。還可以將這個 POJO 用作 DTO,以便將數(shù)據傳輸?shù)?REST 端點。以這種方式傳輸數(shù)據有一些缺點。BeanValidation 注釋與 JPA 注釋混合在一起,這可能導致 Java 類包含大量注釋,而且您的 REST 端點與數(shù)據庫的關系更緊密。

      如果微服務稍大一點或需要處理更復雜的數(shù)據模型,那么最好使用一個單獨層來訪問數(shù)據庫。這個額外層基于 JPA 類來實現(xiàn)所有數(shù)據訪問方法。此層中的類是數(shù)據訪問對象 (DAO)??梢允褂?DAO 類為 REST 端點生成 DTO 類,一方面關注數(shù)據模型 (DAO),另一方面關注客戶端 (DTO)。這種模式的缺點是,必須將 DAO 層中處理的 JPA 類轉換為 DTO,并轉換回來。為了避免創(chuàng)建大量樣板代碼來執(zhí)行此任務,可以使用一些框架來幫助轉換??梢允褂靡韵驴蚣軄磙D換 Java 對象:

      •  ModelMapper

      • MapStruct

      要增加 BeanValidation 帶來的可能性,使用 Spring 獲得額外的特性可能很有用。有關的更多信息,請參見網頁上的'Validation, Data Binding, and Type Conversion'。

      上下文和依賴注入

      如果您的微服務不打算將數(shù)據存儲在事務數(shù)據存儲中,可以考慮使用 CDI 管理 bean 代理 EJB。CDI 1.1 是在 JSR 346 中指定的,包含在 Java EE 7 規(guī)范中。CDI 管理 bean 可能是這些情況下的不錯替代方案。

      有關 CDI 管理 bean 的更多信息,請訪問下面這個網站:

      http://docs.oracle.com/javaee/6/tutorial/doc/giwhl.html

      與 EJB 相比,CDI 本身沒有 Java EE 安全機制,沒有注入持久化上下文。此過程必須由開發(fā)人員自己完成,或使用其他框架完成。舉例而言,Apache DeltaSpike 有許多模塊可用于擴展 CDI 的功能。有關 Apache DeltaSpike 的更多信息,請訪問:

      http://deltaspike./index.html

      可使用其他框架來擴展 CDI 管理 bean 的功能。EJB 有一個可在應用服務器中管理的線程池。CDI 目前沒有與此功能相對應的功能。能夠配置線程池,這在高負載的環(huán)境中很有幫助。

      為了實現(xiàn)不在 Java EE 應用服務器中運行的微服務,CDI 和其他模塊提供了許多對這些環(huán)境有用的功能。

      Java Message Service API

      為了在 Java 領域實現(xiàn)事件驅動架構,JMS API 提供了相關支持,Java Message Service 2.0 JSR 343 中也指定了該 API。JMS 用于與必須通過面向消息的中間件 (MOM) 實現(xiàn)的消息提供程序通信。

      當 JMS 從 1.1 版更新到 2.0 版(包含在 Java EE 7 規(guī)范中)時,進行了大量返工來讓 API 更容易使用。JMS 2.0 兼容更低的版本,所以您可以對新微服務使用現(xiàn)有代碼或使用新的簡化 API。下一個版本不會棄用舊 API。

      依據智能端點和啞管道方法,基于 Java 的微服務必須將 JSON 消息發(fā)送到 JMS 提供程序托管的端點。消息的發(fā)送方被稱為生成者,消息的接收方被稱為使用者。這些端點可具有以下類型:

      • 隊列

      一個隊列中的消息僅由一個使用者使用。隊列中的消息序列可按不同的順序使用。隊列采用端到端的語義使用。

      • 主題

      這些消息可供多個使用者使用。這是發(fā)布/訂閱語義的實現(xiàn)。

      在基于 REST 的微服務中(其中基于 JSON 的請求由客戶端發(fā)出),對消息系統(tǒng)也采用 JSON 格式是一個不錯的主意。其他消息應用程序使用了 XML。如果您的微服務系統(tǒng)中僅有一種格式,則更容易實現(xiàn) JSON。

      示例 22 生成者使用 EJB 將消息發(fā)送到 JMS 隊列

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      @Stateless
      @LocalBean
      public class OrderEJB {
      @Inject
      @JMSConnectionFactory('jms/myConnectionFactory')
      JMSContext jmsContext;
      @Resource(mappedName = 'jms/PaymentQueue')
      Queue queue;
      public void sendMessage(String message) {
      jmsContext.createProducer().send(queue, message);
      }

      需要一個 JMSContext 和一個 Queue 才能發(fā)送消息(示例 22)。如果消息發(fā)送方在 Java EE 應用服務器中運行,則會注入這些對象。示例 22 使用一個 EJB,所以注入了這些資源。必須在應用服務器中對注入的對象進行配置。如果發(fā)生異常,則會拋出一個運行時異常 JMSRuntimeException。

      注入的 JMSContext 在 JTA 事務中的范圍為 transaction。所以如果您的消息生成者是 EJB,您的消息將傳送到事務的上下文中,這樣做可以避免松散的消息。

      要使用來自隊列的消息,使用消息驅動 EJB (MDB) 就能輕松實現(xiàn),如示例 23 所示。使用 MDB 的另一個優(yōu)勢是,消息的使用在事務中完成,所以不會丟失消息。

      示例 23 使用者 – 負責處理來自 JMS 隊列的消息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      @MessageDriven(
      name='PaymentMDB',
      activationConfig = {
      @ActivationConfigProperty(
      propertyName='messagingType',
      propertyValue='javax.jms.MessageListener'),
      @ActivationConfigProperty(
      propertyName = 'destinationType',
      propertyValue = 'javax.jms.Queue'),
      @ActivationConfigProperty(
      propertyName = 'destination',
      propertyValue = 'PaymentQueue'),
      @ActivationConfigProperty(
      propertyName = 'useJNDI',
      propertyValue = 'true'),
      }
      )
      public class PaymentMDB implements MessageListener {
      @TransactionAttribute(
      value = TransactionAttributeType.REQUIRED)
      public void onMessage(Message message) {
      if (message instanceof TextMessage) {
      TextMessage textMessage = (TextMessage) message;
      String text = message.getText();
      ...
      }
      }
      }

      必須使用 @MessageDriven 注釋將 EJB 類型聲明為消息驅動 EJB。在此注釋內,可以設置 MDB 的名稱和一些激活配置。激活配置的屬性將 MDB 與處理隊列或主題的 JMS 消息系統(tǒng)相關聯(lián)。在托管您的 MDB 的應用服務器環(huán)境中,很容易配置這些元素。MDB 本身會實現(xiàn)一個 MessageListener 接口,該接口只有一個方法:onMessage。每當 JMS 提供程序有消息要處理時,它就會調用此方法。使用一個事務屬性注釋該方法,以表明它是在一個事務內調用的。MDB 的默認事務屬性是 TransactionAttributeType.REQUIRED。在該方法內,必須轉換消息對象,而且消息可以提取為字符串。也可使用其他消息類型。

      僅使用 MDB 處理消息是一種不錯的做法。將 MDB 保持為一個技術類。您的剩余業(yè)務代碼應在 MDB 調用的 Java POJO 中實現(xiàn)。此配置使業(yè)務代碼更容易在 JUnits 中測試。

      前面已經提到過,每個 MDB 在一個事務內運行,所以不會丟失消息。如果在處理消息期間發(fā)生錯誤,而且 EJB(處理 MDB)收到此運行時異常,那么該消息會重新傳送到 MDB(錯誤循環(huán))??稍?JMS 提供程序中配置重試次數(shù),它指定了發(fā)生此錯誤的頻率。在達到重試上線次數(shù)后,消息通常被放入一個錯誤隊列中。錯誤隊列中的消息必須單獨處理。

      如果一個業(yè)務事務涵蓋多個微服務,可使用事件驅動架構(參見小節(jié)'跨微服務的數(shù)據共享')。這意味著發(fā)送事件的微服務必須執(zhí)行以下任務:

      • 更改其數(shù)據存儲中的數(shù)據

      • 將消息發(fā)送到第二個微服務

      接收微服務必須執(zhí)行以下任務:

      • 從隊列接收消息

      • 更改它的數(shù)據存儲中的數(shù)據

      為了保持一致,如果數(shù)據存儲是事務性的,這兩個操作必須在一個事務中完成。對于消息系統(tǒng)的生成者和使用者,也要滿足此要求。在這些情況下,必須使用分布式事務。事務伙伴是數(shù)據存儲和 JMS 提供程序(不是兩個微服務的兩個數(shù)據存儲)。

      要跟蹤生成和使用的消息,使用關聯(lián) ID 很有用。消息生成者指定的關聯(lián) ID 與使用者使用的消息相關聯(lián)。這個關聯(lián) ID 也可用在微服務的日志記錄中,以便獲得微服務調用的完整通信路徑。

      Java 提供了一個類來生成唯一 Id:UUID。這個類可用于生成關聯(lián) ID。示例 24 展示了如何設置關聯(lián) ID。

      示例 24 在 JMS 消息中設置關聯(lián) ID

      1
      2
      3
      4
      // JMSContext injected as before
      JMSProducer producer = jmsContext.createProducer();
      producer.setJMSCorrelationID(UUID.randomUUID().toString());
      producer.send(queue, message);

      示例 25 展示了如何獲取關聯(lián) ID。

      示例 25 從 JMS 消息獲取關聯(lián) ID

      1
      2
      // message received as before
      String correlationId = message.getJMSCorrelationID();

      有關 UUID 的更多信息,請訪問下面這個網站:

      http://docs.oracle.com/javase/7/docs/api/java/util/UUID.html

      如果使用非 JMS 提供程序作為面向消息的中間件,JMS 可能不是向此系統(tǒng)發(fā)送消息的正確方法。可結合使用 RabbitMQ(一個 AMQP 代理)和 JMS,因為 Pivotal 為 RabbitMQ 實現(xiàn)了一個 JMS 適配器。有關 RabbitMQ 的更多信息,請訪問下面這個網站:

      http://www./

      Apache Qpid 也為 AMQP 協(xié)議實現(xiàn)了一個 JMS 客戶端。這些只是一些示例,表明使用 JMS 與非 JMS 提供程序通信也很有用。但是,根據您的需求,使用消息系統(tǒng)的原生 Java 驅動程序有可能會更好一些。有關 Apache Qpid 的更多信息,請訪問下面這個網站:

      http://qpid./index.html

      Spring 對 JMS 消息提供程序的支持是處理 JMS 消息的另一種方案。有關 Spring 的更多信息,請訪問下面這個網站:

      http://docs./spring/docs/current/spring-framework-reference/html/jms.html

      Java 和其他消息協(xié)議

      根據您需要的面向消息的中間件中的特性,您可能需要一個非 JMS 消息提供程序系統(tǒng)。MQTT 是一種最適合物聯(lián)網 (IoT) 和 AMQP 的需求的消息協(xié)議。它是為實現(xiàn)跨供應商移植而開發(fā)的。

      JMS 不能用于與這些系統(tǒng)通信。通過使用它們提供的客戶端,可以使用它們提供的所有特殊功能。有關 MQTT 的更多信息,請訪問下面這個網站:

      http:///

      Apache Kafka 是一個非 JMS 提供程序,但它提供了一個 Java 驅動程序。人們提出了一種增強請求,希望實現(xiàn)一個適配器,讓客戶端能與 Apache Kafka 傳輸 JSM 消息,但此問題仍待解決。所以最好使用 Kafka 提供的 Java 驅動程序。有關 Kafka 的更多信息,請訪問下面這個網站:

      http://kafka./

      RabbitMQ 是一個消息代理系統(tǒng),它實現(xiàn)了 AMQP 協(xié)議,而且還提供了一個 Java 客戶端。有關 RabbitMQ 的更多信息,請訪問下面這個網站:

      https://www./

      Spring 有一個與基于 AMQP 的消息系統(tǒng)通信的庫。Spring 還提供了對 MQTT 協(xié)議的支持。有關 Spring 的更多信息,請訪問下面這個網站:

      http://projects./spring-amqp/

      可以看到,支持結合使用 Java 和非 JMS 消息系統(tǒng)。

      總結

      本文重點介紹了如何使用基于 Java 的微服務實現(xiàn)微服務在數(shù)據處理方面保持可管理。下一部分我們將回到第一部分中講到的演化策略,將介紹可在實踐中考慮和應用的可能戰(zhàn)略。好了,學習愉快,下次再見!

      出處:https://www.ibm.com/developerworks/cn/java/j-cn-java-and-microservice-4/index.html?ca=drs-

        本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
        轉藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多