基於Java 9模組系統和Vert.x開發持續整合系統

來源:互聯網
上載者:User

基於Java 9模組系統和Vert.x開發持續整合系統
本文要點

  • Vert.x相容Java 9,可一起用於構建應用程式。
  • 很多Java類庫仍然不支援模組化。
  • 對“自動模組”要格外小心(一些類庫還沒有成為模組)。
  • Java內建的Nashorn JavaScript運行環境對於Vert.x的應用程式來說十分有用。

這篇文章將介紹如何使用Eclipse Vert.x設計和開發一個基於訊息驅動的響應式持續整合(CI)系統。我們將利用Java平台模組系統(JPMS)來構建一個由多個模組組成的應用程式,模組之間通過定義好的介面進行通訊。

有了JPMS,架構師和開發人員就可以使用模組來重構大型的遺留系統,或者用它們來建立新的應用程式。不過,要在模組系統中使用已有的Java類庫並不是件容易的事。因此,我們也會探討在使用JPMS過程中可能遇到的各種問題,以及如何解決這些問題。

先讓我們來定義這個CI系統的最小可用產品(MVP),我們將把它構建成Docker原生系統。這個系統需要提供如下特性,並通過REST API暴露出來:

  1. 支援針對倉庫的CRUD操作。一個倉庫代表一個項目,並帶有Git倉庫的串連地址。
  2. 支援“管道即代碼”。管道定義了構建流程,並使用JavaScript來定義,JavaScript指令檔可以與代碼儲存在一起。
  3. 提供用於啟動或關閉管道的API。管道的一個執行個體就代表一次構建過程。

定義好MVP後,就可以開始構建我們的系統了。首先要建立項目的骨架,可以使用IntelliJ提供的多模組Gradle項目模板來建立骨架。因為要使用JDK 9,所以最好可以選擇最新版的Gradle(在寫這篇文章是最新版是4.4.1)。我們還需要添加Jigsaw外掛程式,並把代碼相容性設定為Java 9。項目的主檔案“build.gradle”應該看起來像下面這樣:

與其他大多數系統一樣,我們將會有一個公用庫,用來放置實體類、工具類、共用常量、查詢解析器等。我們把這個公用庫定義成一個Java 9模組。

之前已經講過,Java 9模組是介面、類和資源檔的集合,具有自描述的特點,而且有自己的名字。JPMS引入了“module-info.java”檔案,開發人員用它定義模組的公用契約和對其他模組的依賴。我們也將使用這個檔案來命名我們的模組,並指定對其他模組的依賴以及模組自身暴露出來的公用包。

是module-info.java檔案的範例程式碼:

每個“module-info.java”檔案都以關鍵字“module”作為開頭,後面跟上模組的名字。用在包命名上的反向網域名稱命名方式也可以用在模組的命名上。

代碼塊中有兩個新的關鍵詞——“exports”和“requires”。“exports”用於聲明由該模組暴露出來的公用包,也就是模組的公用API。“requires”用於聲明對其他模組的依賴。

那麼,問題來了,如果一個Java 9模組的依賴包並不是模組,那該怎麼辦?這個時候,自動模組就派上用場了。

正如它的名字告訴我們的那樣,非模組的JAR包會被自動轉成模組,並基於JAR包的名字來產生模組名。模組名的產生遵循這樣的規則:以JAR包檔案開頭,去掉副檔名,用點號替代連字號,如果有版本號碼就把版本號碼去掉。這樣的話,“vert-core-3.5.0.jar”對應的模組名就是“vertx.core”。不過,這種方式不一定都能奏效,後面我們會舉一個與Netty依賴包相關的例子。

除了核心模組,我們還要定義其他一些模組,用於訪問資料庫、使用者認證、運行引擎以及與CI系統中的其他外掛程式互動。

在介紹了Java模組化的一些概念後,接下來讓我們來聊聊Vert.x。Vert.x是一個工具套件,提供了非阻塞的API,也就是說,Vert.x應用程式只需要使用很少的線程就可以處理大量的並發請求。Vert.x採用了multi-reactor模式來達到這個目的。

熟悉JavaScript的開發人員或許還記得單線程事件迴圈模型,multi-reactor模式與之類似,只不過它使用了多個線程。Vert.x根據給定伺服器的CPU核心數建立相應個數的事件迴圈對象。

Vert.x還提供了另一種基於actor的並行存取模型。在Vert.x生態系統中,actor被稱為“verticle”,verticle之間通過JSON訊息進行通訊,這些訊息通過事件匯流排進行傳送。我們還可以指定部署多少個verticle執行個體。

事件匯流排可以是一個叢集,使用叢集管理器來管理,比如Hazelcast或Zookeeper。我們可以把運行在Vert.x執行個體上的verticle或verticle組合看成是微服務。mutli-reactor模型、verticle和事件匯流排讓Vert.x應用程式具備了高響應式、高彈性的特點,因此,我們可以說Vert.x應用程式是反應式的。

現在讓我們來看看這個CI系統的整體流程:

如所示,有好幾個verticle通過Vert.x事件匯流排進行通訊。要注意,圖中的外掛程式也是verticle。Server verticle是CI系統的入口,對外暴露了一個REST API,命令列或GUI用戶端可以通過這個API指定代碼倉庫的串連地址、建立和運行構建管道。

下面的代碼告訴我們如何在Vert.x中定義API和路由:

我們使用Vert.x的Web類庫來定義REST API,而且所有的路由均以“/api/v1/”作為首碼。Vert.x還提供了很多其他類庫,用於快速開發反應式應用程式。

例如,我們可以使用Web API類庫來設計一個基於OpenAPI 3的應用程式API,這個類庫會幫我們處理好請求驗證和安全驗證問題。Vert.x的OAuth類庫可用來提高應用程式API的安全性,OAuth廠商可以是Google、Facebook,也可以自訂。

在上一張圖片中,Engine verticle負責協調管道的執行。在用戶端調用Server verticle提供的API之後,Server verticle向Engine verticle發送訊息,Engine verticle在收到訊息之後會初始化一個新的flow對象。

flow對象實際上是一個簡單的狀態機器,用來跟蹤管道的執行狀態。在任意時刻,flow對象可能處於這三種狀態中的一種:setup、run或teardown。它會根據輸入訊息來改變狀態。在進入一個新的狀態時,flow對象會觸發一個事件,並將事件發送到事件匯流排。

註冊到事件匯流排上外掛程式會處理這些訊息,並把處理結果通過事件匯流排非同步傳回。下面的代碼示範了如何註冊一個訊息處理器、建立flow對象以及處理流入的訊息:

Engine verticle也負責定位和部署其他外掛程式或verticle。我們使用了在Java 6中引入並在Java 9中改進過的服務載入器機制,用它在伺服器啟動過程中定位和部署外掛程式。為了更好地理解服務載入機制,有必要討論一下服務和服務提供者。

服務其實就是一個已知的介面或類(通常是抽象類別),而服務提供者則是服務的具體實現。ServiceLoader類用於載入實現了給定服務的服務提供者。我們可以在模組中聲明它使用了某個特定的服務,然後使用ServiceLoader來定位和載入部署在運行環境中的服務提供者。

例如,server模組聲明了它要使用Plugin介面,workspace模組則聲明它將提供兩個實現了Plugin介面的服務。

因此,server模組在啟動的時候,它會調用ServiceLoader,找到兩個外掛程式,然後把它們部署成verticle:

外掛程式會完成很多工作,包括註冊訊息處理器,用於處理感興趣的管道事件。例如,workspace外掛程式負責同步Git代碼,而指令碼解析器外掛程式負責掃描workspace,找出和執行管道指令檔(使用JavaScript編寫)。執行完指令檔會產生一些shell命令,Docker容器中的指令碼執行器外掛程式會執行這些命令。因為Vert.x使用了Java內建的Nashorn引擎,所以完全可以運行JavaScript代碼。要知道,Vert.x還可以支援JavaScript、Kotlin和Groovy。

下面是管道指令檔的部分代碼:

收到訊息後,指令碼執行器外掛程式將會下載Docker鏡像、建立容器並執行shell命令。

Docker的REST API通常是通過基於unix domain socket的HTTP來暴露的,並非傳統的基於TCP socket的HTTP(S)。這也是Vert.x得以發揮其作用的地方之一,我們可以使用Vert.x的非同步用戶端與Docker進行互動,而不是使用普通的同步阻塞式方案。

如果項目中包含了由Netty提供的原生傳輸類庫,Vert.x就會轉而使用原生傳輸。如果我們在“build.gradle”和“module-info.java”檔案中指定了類似“netty-transport-native-kqueue”這樣的依賴,就會發生這樣的情況。

或許Vert.x下一個版本會支援基於unix domain socket的HTTP。目前,我們可以通過修改Vert.x類庫的少量代碼來解決這個問題。與Docker引擎互動的外掛程式代碼看起來是這樣的:

加入非模組JAR包“netty-transport-native-kqueue-4.1.15.Final-osx-x86_64.jar”作為依賴將會自動產生一個模組名,不過因為JAR包含了Java關鍵字native,我們的應用程式無法正常編譯。

Netty將在下一個版本中解決這個問題,而在問題得到解決之前,我們可以在JAR包的manifest檔案中加入“Automatic-Module-Name”來繞過這個問題。為此,我們需要解壓JAR包,修改“MANIFEST.MF”檔案,加入“Automatic-Module-Name: io.netty.transport.kqueue”,然後通過下面的命令重新打包:

可以通過下面的命令來驗證在manifest檔案中指定的自動模組名是否可以被正確識別:

我們還要使用相同的命令解決其他非模組JAR包的命名問題。

接下來就可以構建和運行我們的CI系統了。下面是運行應用程式的命令:

為了支援JPMS,我們在“javac”和“java”命令中增加了一些新的參數,這些參數告訴Java編譯器和運行時,使用模組路徑和模組JAR包來替代原先的類路徑。

一些值得注意的參數:

“-p”或“—module-path”用於告訴Java系統在指定的目錄中尋找Java模組。

“-m”或“—module”用於指定模組和主類。

在這篇文章裡,我們基於Vert.x設計了一個模組化的微服務應用程式。我們使用了JPMS和JDK 9,並構建了一個Docker原生的CI系統。可以在GitHub上下載相關代碼,瞭解如何基於Vert.x和模組化系統開發一個小型的自包含模組化Java應用程式。

關於作者

Uday Tatiraju 是Oracle的首席工程師,在電子商務平台、搜尋引擎、後端系統和Web與移動編程領域擁有十年的開發經驗。

 

 

查看英文原文:Building a CI System with Java 9 Modules and Vert.x Microservices

本文永久更新連結地址:https://www.bkjia.com/Linux/2018-03/151252.htm

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.