Vertx-Web初探(上)
Posted Vertx北京用户组
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vertx-Web初探(上)相关的知识,希望对你有一定的参考价值。
首先非常感谢虞先生(昵称:戒子猪)百忙之中抽空写了这篇Vertx-web的文章,真得是干货满满,希望广大Vertx爱好者不要错过。
由于内容非常详尽丰富而且图比较多,建议在电脑上查看,网址:
http://mp.weixin.qq.com/s?__biz=MzA4MjUwNzQ0NQ==&mid=402228882&idx=1&sn=d9eebb0bbc306de4516a018dbc67af5e#rd
目录
基本准备
Vert.x的启动
Router的使用
Event Bus
关于Handler
分析章节
总结
本文主要针对Vert.x的子项目Vert.x Web进行初步探索,了解各种组件用法以及项目实施方式,同时解剖我们开发的项目中使用Vert.x的部分内容。
基本准备
创建一个Maven项目,其项目结构如下:
源代码包:
src/main/vertx
资源路径CLASSPATH:
src/main/resources
下边截图中
${vertx.version}
的值为3.2.0
,及Vertx使用的版本
Maven的pom.xml文件内容如下:
因为vertx-web
项目已经引用了vertx-core
,在引用依赖库的时候,可以只引用vertx-web
即可,具体内容可参考Maven的pom.xml
配置。
1.2.基本配置文件
基本配置文件主要包含两个文件(位于src/main/resources
目录下):
vertx.properties
:自定义Vert.x实例的启动配置;cluster.xml
:在Cluster集群模式下使用io.vertx.spi.cluster.hazelcast.HazelcastClusterManager
的基本配置文件,这个XML文件格式和Hazelcast
默认配置文件hazelcast.xml
格式一致,Vertx-web
中仅仅修改了配置文件的名称。【参考Hazelcast
官方文档】
2.Vert.x的启动
因为我们项目使用了编程方式执行启动操作,所以本文对Vert.x
从命令行的启动不做介绍,启动主要应用两种模式:
单实例的
Vert.x
启动流程;集群多实例的
Vert.x
启动流程;
2.1.基本配置文件
看看vertx.properties
的详细内容,下边配置主要是Vertx
实例的配置信息,用于构造io.vertx.core.VertxOptions
对象用
*:只要Vertx
的实例不在同一台机器共享端口号,那么每台机器都可以发布多个Vertx
实例,而不同的Vertx
实例可使用各自独立的配置,为了便于管理,可直接设置Vertx
实例的名字来实现不同的部署流程【上边配置中的名字为VXWEB-DEMO
】。
2.2.构造Vertx
配置项
在发布Vert.x实例时,需要传入一个io.vertx.core.VertxOptions来实现针对Vert.x的配置项的管理,该实例的构造代码如下:
所有配置项的基本API说明如下:
setEventLoopPoolSize
设置Vert.x
实例使用的Event Loop
线程的数量,默认值为:2 * Runtime.getRuntime().availableProcessors()
(可用的处理器个数);setWorkerPoolSize
设置Vert.x
实例中支持的Worker
线程的最大数量,默认值为20
;setInternalBlockingPoolSize
设置内部阻塞线程池最大线程数,这个参数主要被Vert.x
的一些内部操作使用,默认值为20
;setClustered
是否开启Vert.x
的Cluster
集群模式,默认值为false
;setClusterHost
【Cluster
集群模式有效】设置集群运行的默认hostname
,默认值为localhost
;setClusterPort
【Cluster
集群模式有效】设置集群运行的端口号,可自定义固定端口号,默认值为0
(随机分配);setClusterPingInterval
【Cluster
集群模式有效】使用ping
命令检测Cluster
的时间间隔,默认20000
,单位毫秒ms
,即20
秒;setClusterPingReplyInterval
【Cluster
集群模式有效】集群响应ping
命令的时间间隔,默认20000
,单位毫秒ms
,即20
秒;setBlockedThreadCheckInterval
阻塞线程检查的时间间隔,默认1000
,单位ms
,即1
秒;setMaxEventLoopExecuteTime
Event Loop
的最大执行时间,默认2l * 1000 * 1000000
,单位ns
,即2
秒;setMaxWorkerExecuteTime
Worker
线程的最大执行时间,默认60l * 1000 * 1000000
,单位ns
,即60
秒;setHAEnabled
是否支持HA
架构,默认值false
;(*:如果Vert.x
启用了HA
模式,其中一个Vert.x
实例运行出现了异常或者死掉,那么这个Vert.x
实例中运行的Verticle
会执行重发布Redeploy
的过程将运行在该实例中的Verticle
实例重新发布到其他正常运行的Vert.x
实例中【Verticle Fail-Over
】。)setHAGroup
【HA
模式有效】支持了HA
模式过后,可根据不同的组名将Vert.x
进行逻辑分组,此方法设置当前Vert.x
的逻辑组名,默认__DEFAULT__
;setQuorumSize
【HA
模式有效】支持了HA
模式过后,此方法设置冲裁节点的数量,默认值1
;(*:这个值一旦设置过后,如果要发布Verticle
组件,则需要运行环境中的节点数量达到这个值才可执行发布,比如quorum
的值设置成3
,如果仅仅运行了两个Vert.x
实例,那么这种情况下Verticle
不会被发布,必须要运行至少三个Vert.x
实例,才会执行发布Deploy
流程,Undeploy
流程类似)setWarningExceptionTime
如果线程阻塞时间超过了这个阀值,那么就会打印警告的堆栈信息,默认为5l * 1000 * 1000000
,单位ns
,即5
秒;
注意
上边的API和使用vertx
运行时的命令行参数是保持一致的,例如:
最后需要注意的一点是vertx run
命令中有些参数不是Vert.x
实例参数,而是Verticle
实例使用参数,例如:-instances 10
以及-worker
,这两种实例使用的Java Class
是不一样的。
2.3.书写一个Standard
的Vertcle
在src/main/vertx
中书写一个默认的Standard
类型的Verticle
,其代码内容如下(为了符合初学者的口味,提供完整的代码方便理解)
注意:上边代码中createHttpServer
还有一个重载方法,可传入一个io.vertx.core.http.HttpServerOptions
实例,该实例可以进行Web Server
级别的配置,也可以理解成为包含Vert.x
实例的容器配置项信息,这里就不做说明了。
2.4.构造Verticle
的配置项
上文中已经使用两种Options
,在最终运行Verticle
实例之前,还需要设置另外一个Options
用于配置Verticle
实例:io.vertx.core.DeploymentOptions
,参考下边代码:
因为RouterVerticle
是一个Standard
的类型,这里提供常用的关于Verticle
的选项设置,其余的配置项可参考DeploymentOptions
类的源代码查看,从Vert.x 3.0
开始,Verticle
类型从2.0
的两种(Standard、Worker)升级成了三种:
Standard Verticles
Worker Verticles
Multi-threaded Worker Verticles(并行应用,超过一个线程执行该应用)
上边的API对应下边的几个运行参数:
2.5.单实例运行
各种Options
都设置好了,接下来可以看看主函数代码:
Import部分
Main函数部分
TroubleShoot部分
因为启用了
Cluster
集群模式,所以不能直接使用factory.vertx()
方法来创建Vert.x
实例,如果在Cluster
模式运行上边代码会遇到下边的错误信息:
将配置文件中的集群模式先关闭
在运行的时候有一个和
Performance
的依赖库相关的jar
,如果没有放在类路径中,则会有下边的调试日志输出:
在pom.xml
文件中添加下边依赖库则可以除去这段警告信息:
配置好过后就会输出下边合法的DEBUG
信息了
浏览器测试
运行了主函数过后,该函数并不会退出【在Eclipse中没有<terminated>
标记】,先从浏览器中打开http://localhost:8080
可以看到下边界面:
针对这个简单的单实例应用的例子,简单分析一下:
在
Verticle
被发布的时候,它的start()
方法会被调用,注:所有的Verticle
实例在Deploy
时启动阶段是异步操作,看看deployVerticle
方法的源代码就可以知道,只是将CompletionHandler
参数设置成null
的调用流程:
记得区分VerticleFactory
和VertxFactory
属于不同的对象,注意下边的这段代码:
这段代码位于
DeploymentManager
中,这里遵循下边的原则:如果发布
Worker
类型的Verticle
时,VerticleFactory
实际上在底层会以同步的方式做Deploy
操作——executeBlocking
操作,它会取代event-loop
线程,这个时候blockingCreate()
的返回值是true
,并且生成的线程名会有所区别;如果发布的不是
worker
类型,则默认的Verticle
会使用当前的event-loop
线程,这种时候blockingCreate()
方法会返回false
我们在真实项目中使用了两种
Standard
的Verticle
,一个用于前端Web UI
,一个用于Rest
接口,后端使用了四种Worker
,从Deploy
的速度上可以验证上边两点,当调用了deployVerticle
方法过后,如果将Standard
的实例数量设置得和Worker
数量一致,则Standard
的Verticle
发布和启动速度比Worker
快,实例数量大的时候这个延迟会很明显;在
start
中添加线程名称打印代码,可得到下边的输出信息,从后缀的数字可以知道这些实例的Deploy
是异步的:
Handler
内部代码属于单线程操作,如果从浏览器访问,后台会输出下边信息:这里的打印语句执行了两次是因为
HttpServer
默认有一个favicon的请求,实际上是两次GET请求,添加path
信息就可以得到下边输出:同一个类型的
Verticle
实例可共享端口,如同上边的代码演示的,10
个Verticle
实例共享了8080
的端口,不同的Verticle
实例目前没找到共享端口的方法,暂时定义为不能共享端口,如果两个不同的Verticle
监听了同一个端口会出现JVM_BIND
的端口占用异常
关于executeBlocking
同步块的使用问题
官方关于这个方法有明确解释,如果需要实现同步阻塞方式的代码,那么在Vert.x
中禁止手动阻塞Event Loop
,下边几种方式都是不推荐的【这里不做翻译】:
Thread.sleep()
Waiting on a lock
Waiting on a mutex or monitor ( e.g.synchronized section )
Doing a long lived database operation and waiting for a result
Doing a complex calculation that takes some significant time.
Spinning in a loop
为了解决同步需求,官方提供了executeBlocking
方法用于在Vert.x
中创建阻塞式代码块。我们在项目实施中,所有的Router
的配置数据都是保存在文件系统、元数据库中的,所以在Deploy
过程如果读取配置数据不设置同步则会出现阻塞警告异常,这个时候:要么需要考虑使用executeBlocking
实现同步操作,要么想办法使用异步模式提供文件异步读写和数据库异步读写来保证线程不会出现阻塞。
先看下边的代码段,routeConfigurator.getRouter()
会访问Metadata Server
去读取Router
的配置数据:
上边的代码在Verticle
的Deploy
过程会抛出下边的警告信息:
为了解决这个问题,将代码做少许改动即可【把数据库访问部分的代码放到executeBlocking
内】:
注意区分Handler
内部的代码和Verticle
的start()
方法的代码:
上边高亮部分的代码的写法是JDK 8
中的Lambda
表达式,实际上属于代码定义,在start()
方法调用的时候它并不会执行,而是在真正发送请求的时候执行;如果不熟悉Lambda
表达式则可使用下边的代码写法,其效果和Demo
中的效果是一致的:
Handler
部分的内容后边会提及到,这里暂时先不管,包括Handler
必须实现的接口以及返回类型等,都可以不使用JDK 8
的语法去实现,但Vert.x
本身使用了很多Lambda
的语法,所以Vert.x
的最低JDK版本要求是8.0
,最后需要说明的就是Standard
的Verticle
可以不调用server.listen()
方法,即不监听任何端口,这样主程序也不会退出,只是无法从浏览器访问而已,Worker
类型的Verticle
会经常使用这种方式来进行部署。
2.6.Cluster集群模式运行
上边一个章节所有的代码演示了非集群模式的运行代码,接下来看看集群模式的主函数怎么写,集群模式和单实例模式唯一的不同是在代码中需要提供一个ClusterManager
,而Vert.x
默认使用了HazelcastClusterManager
实现集群管理。首先在pom.xml
文件中添加下边的XML
片段:
因为项目里使用了dependencyManagement
节点统一管理使用的版本信息,所以这里没有截version
节点的版本信息【3.2.0】,如果运行时有问题或者IDE报错请自己加上<version>
节点信息,可参考Maven
资料补充上边截图片断。
添加好了上边的依赖库信息后,就可以使用HazelcastClusterManager
了,看看改动过后的主函数代码:
上边代码运行后,可直接在Console
中看到Hazelcast
的输出信息【这个信息在单实例运行中不会输出】如下,日志级别可以通过在资源路径(src/main/resources
)中提供日志配置文件进行调整:
日志文件名:
vertx-default-jul-logging.properties
文件片段
发布成功过后,和单实例一样可以通过浏览器访问了,测试部分这里就不再重复,端口依然是8080——也就是Server
监听的那个端口。
额外测试
针对Cluster
可以执行另外的测试,上边的输出可以看到目前只有一个节点Member
在环境中,因为Hazelcast
是广播的方式,所以可以从后台启动另外一个Hazelcast
实例,并且启动Hazelcast
的后台Console
【参考Hazelcast
的官方文档】,运行好过后就可以看到下边的输出信息,Member
直接变成了两个,这样就启动了两个Hazelcast
实例:
并且在Console
中可使用Hazelcast
命令监控集群环境中的数据【上边截图的Console运行节点和下边截图的Console运行节点不一样,注意this
的位置】:
使用代码方式启动了Hazelcast
的第二个实例后,Eclipse中可通过下边的菜单进行两个Console
的切换:
第一个【
ClusterStarter
】是启动Vert.x
集群时开启的Hazelcast
实例,由HazelcastClusterManager
启动;第二个【
HazelcastStarter
】是直接通过代码启动Hazelcast
实例并且开启的后台Console
;
2.7.总结
到这里,关于Vert.x
的发布和启动部分就不需要再进行说明了,完成了这部分内容过后,就可以进入Vert.x Web
的基础学习了,言归正传,接下来看看关于Vert.x Web
这个子项目的内容,通过一些示例来看看Vert.x Web
究竟带来了什么?而上边所有的示例都没有使用Vert.x
的异步start()
方法,这里就不深究了,有兴趣的可以自己去琢磨琢磨异步的start()
和stop()
方法。
如果在Vert.x
启动的时候会做很多复杂的操作,那么就可以考虑使用异步的start()
方法来完成这些复杂的操作,防止线程阻塞。
因为内容非常详尽和丰富,将分多次来介绍这篇文章,这次就先到这边,敬请期待下一部分。
编辑:子期
以上是关于Vertx-Web初探(上)的主要内容,如果未能解决你的问题,请参考以下文章