设为首页 加入收藏

TOP

基于Java 9模块系统和Vert.x开发持续集成系统(一)
2018-03-18 16:21:45 】 浏览:308
Tags:基于 Java 模块 统和 Vert.x 开发 持续 集成 系统

这篇文章将介绍如何使用Eclipse Vert.x设计和开发一个基于消息驱动的响应式持续集成(CI)系统。我们将利用Java平台模块系统(JPMS)来构建一个由多个模块组成的应用程序,模块之间通过定义好的接口进行通信。


有了JPMS,架构师和开发者就可以使用模块来重构大型的遗留系统,或者用它们来创建新的应用程序。不过,要在模块系统中使用已有的Java类库并不是件容易的事。因此,我们也会探讨在使用JPMS过程中可能遇到的各种问题,以及如何解决这些问题。


先让我们来定义这个CI系统的最小可用产品(MVP),我们将把它构建成Docker原生系统。这个系统需要提供如下特性,并通过REST API暴露出来:


定义好MVP后,就可以开始构建我们的系统了。首先要创建项目的骨架,可以使用IntelliJ提供的多模块Gradle项目模板来创建骨架。因为要使用JDK 9,所以最好可以选择最新版的Gradle(在写这篇文章是最新版是4.4.1)。我们还需要添加Jigsaw插件,并把代码兼容性设置为Java 9。项目的主文件“build.gradle”应该看起来像下面这样:


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


与其他大多数系统一样,我们将会有一个公共库,用来放置实体类、工具类、共享常量、查询解析器等。我们把这个公共库定义成一个Java 9模块。


之前已经讲过,Java 9模块是接口、类和资源文件的集合,具有自描述的特点,而且有自己的名字。JPMS引入了“module-info.java”文件,开发者用它定义模块的公共契约和对其他模块的依赖。我们也将使用这个文件来命名我们的模块,并指定对其他模块的依赖以及模块自身暴露出来的公共包。


下图是module-info.java文件的示例代码:


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


每个“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模式来达到这个目的。


熟悉java script的开发者或许还记得单线程事件循环模型,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系统的整体流程:


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


如上图所示,有好几个verticle通过Vert.x事件总线进行通信。要注意,图中的插件也是verticle。Server verticle是CI系统的入口,对外暴露了一个REST API,命令行或GUI客户端可以通过这个API指定代码仓库的连接地址、创建和运行构建管道。


下面的代码告诉我们如何在Vert.x中定义API和路由:


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


我们使用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对象以及处理流入的消息:


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


Engine verticle也负责定位和部署其他插件或verticle。我们使用了在Java 6中引入并在Java 9中改进过的服务加载器机制,用它在服务器启动过程中定位和部署插件。为了更好地理解服务加载机制,有必要讨论一下服务和服务提供者。


服务其实就是一个已知的接口或类(通常是抽象类),而服务提供者则是服务的具体实现。ServiceLoader类用于加载实现了给定服务的服务提供者。我们可以在模块中声明它使用了某个特定的服务,然后使用ServiceLoader来定位和加载部署在运行环境中的服务提供者。


例如,server模块声明了它要使用Plugin接口,workspace模块则声明它将提供两个实现了Plugin接口的服务。


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


因此,server模块在启动的时候,它会调用ServiceLoader,找到两个插件,然后把它们部署成verticle:


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


插件会完成很多工作,包括注册消息处理器,用于处理感兴趣的管道事件。例如,workspace插件负责同步Git代码,而脚本解析器插件负责扫描workspace,找出和执行管道脚本文件(使用java script编写)。执行完脚本文件会生成一些shell命令,Docker容器中的脚本执行器插件会执行这些命令。因为Vert.x使用了Java内置的Nashorn引擎,所以完全可以运行java script代码。要知道,Vert.x还可以支持java s

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C与C++内存机制比较 下一篇JavaScript的三种类型检测typeof ..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目