写点什么

基于 Java 9 模块系统和 Vert.x 开发持续集成系统

  • 2018-03-07
  • 本文字数:3925 字

    阅读完需:约 13 分钟

本文要点

  • 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 厂商可以是谷歌、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

2018-03-07 16:423811
用户头像

发布了 322 篇内容, 共 141.3 次阅读, 收获喜欢 146 次。

关注

评论

发布
暂无评论
发现更多内容

Ableton Live 11 Suite for Mac v11.3.11中文版 音乐制作软件

Rose

AI时代的阅读革新!微信读书基于腾讯云ES的“AI问书”RAG最佳实践

腾讯云大数据

ES ;RAG

双良集团:不断进阶的数智化

用友BIP

增速迅猛| 焱融科技跻身中国文件存储市场份额排名报告

焱融科技

Win11镜像下载

Rose

C/C++ 集成开发环境(IDE) JetBrains CLion 2023 for Mac v2023.3.2中文激活版

Rose

研发视角浅谈R2流量回放测试

京东科技开发者

DeFi新纪元:加密货币交易与筹码换手创新玩法

区块链软件开发推广运营

dapp开发 区块链开发 链游开发 NFT开发 公链开发

软工智库|低代码篇(四)——低代码开发平台如何应用?

电子标准院软工研究室

给你安利一个程序员上岸公务员的开源项目

不在线第一只蜗牛

开源 程序员 上岸

面试前:各方面都匹配 面试后:恐怕是凉了

王中阳Go

Go 数据库 面试 后端 面经

Acrobat Pro DC 2024 PDF编辑转换器 v24.001.20604中文激活版

Rose

软工智库|低代码篇(五)——低代码开发平台有何建议?

电子标准院软工研究室

【YashanDB知识库】表收集统计信息默认阈值引起SQL执行效率差

YashanDB

yashandb 崖山数据库

PDF Checkpoint for mac(PDF自动化批处理工具)v1.9.21激活版

Rose

使用coconutBattery Plus, 您始终了解当前的电池健康状况。

Rose

探讨篇(四):分布式数据访问解决方案

京东科技开发者

Set A Light 3D Studio for Mac(3D摄影棚布光工具)v2.58d永久试用版

Rose

FL studio 20破解版,水果音乐制作编曲软件

Rose

AI+制造丨引领智能制造迈向新纪元

用友BIP

攻防演练需持之以恒,智能运维提升企业长期安全防御能力

用友BIP

Java 在成本资源控制下, 千万级别数据查询优化

伤感汤姆布利柏

Acrobat Pro DC 2021 for Mac(专业PDF编辑软件)v2021.007.20091中文版

Rose

Reallusion Cartoon Animator 2D动画设计制作软件 v4.51.3511.1中文激活版

Rose

ABBYY FineReader PDF 15 for Mac(ocr文字识别软件)v15.2.13中文激活版

Rose

Visio Viewer for Mac(Visio文件查看工具)v3.1.0激活版

Rose

2024 MWC 上海 | 七牛云 Miku 快直播发布

七牛云

直播 直播低延迟

Python 性能分析的几个方法,找到你代码中的那个她!

我再BUG界嘎嘎乱杀

Python 编程 后端 性能分析 开发语言

复杂表单一键填充,让信息输入更轻松

HarmonyOS SDK

HarmonyOS

Tableau Desktop 2019 最好用的数据分析工具 mac软件

Rose

基于Java 9模块系统和Vert.x开发持续集成系统_Java_Uday Tatiraju_InfoQ精选文章