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


目录


  1. 基本准备

  2. Vert.x的启动

  3. Router的使用

  4. Event Bus

  5. 关于Handler

  6. 分析章节

  7. 总结


本文主要针对Vert.x的子项目Vert.x Web进行初步探索,了解各种组件用法以及项目实施方式,同时解剖我们开发的项目中使用Vert.x的部分内容。

  1. 基本准备

创建一个Maven项目,其项目结构如下:



  • 源代码包:src/main/vertx

  • 资源路径CLASSPATH:src/main/resources

  • 下边截图中${vertx.version}的值为3.2.0,及Vertx使用的版本

Maven的pom.xml文件内容如下:


Vertx-Web初探(上)

因为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-Web初探(上)
*:只要Vertx的实例不在同一台机器共享端口号,那么每台机器都可以发布多个Vertx实例,而不同的Vertx实例可使用各自独立的配置,为了便于管理,可直接设置Vertx实例的名字来实现不同的部署流程【上边配置中的名字为VXWEB-DEMO】。


2.2.构造Vertx配置项

在发布Vert.x实例时,需要传入一个io.vertx.core.VertxOptions来实现针对Vert.x的配置项的管理,该实例的构造代码如下:

Vertx-Web初探(上)
所有配置项的基本API说明如下:

  • setEventLoopPoolSize
    设置Vert.x实例使用的Event Loop线程的数量,默认值为:2 * Runtime.getRuntime().availableProcessors()(可用的处理器个数);

  • setWorkerPoolSize
    设置Vert.x实例中支持的Worker线程的最大数量,默认值为20

  • setInternalBlockingPoolSize
    设置内部阻塞线程池最大线程数,这个参数主要被Vert.x的一些内部操作使用,默认值为20

  • setClustered
    是否开启Vert.xCluster集群模式,默认值为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-Web初探(上)

最后需要注意的一点是vertx run命令中有些参数不是Vert.x实例参数,而是Verticle实例使用参数,例如:-instances 10以及-worker,这两种实例使用的Java Class是不一样的。

2.3.书写一个StandardVertcle

src/main/vertx中书写一个默认的Standard类型的Verticle,其代码内容如下(为了符合初学者的口味,提供完整的代码方便理解)

Vertx-Web初探(上)
注意:上边代码中createHttpServer还有一个重载方法,可传入一个io.vertx.core.http.HttpServerOptions实例,该实例可以进行Web Server级别的配置,也可以理解成为包含Vert.x实例的容器配置项信息,这里就不做说明了。

2.4.构造Verticle的配置项

上文中已经使用两种Options,在最终运行Verticle实例之前,还需要设置另外一个Options用于配置Verticle实例:io.vertx.core.DeploymentOptions,参考下边代码:

Vertx-Web初探(上)

因为RouterVerticle是一个Standard的类型,这里提供常用的关于Verticle的选项设置,其余的配置项可参考DeploymentOptions类的源代码查看,从Vert.x 3.0开始,Verticle类型从2.0的两种(Standard、Worker)升级成了三种:

  • Standard Verticles

  • Worker Verticles

  • Multi-threaded Worker Verticles(并行应用,超过一个线程执行该应用)

上边的API对应下边的几个运行参数:

Vertx-Web初探(上)



2.5.单实例运行

各种Options都设置好了,接下来可以看看主函数代码:

Import部分

Vertx-Web初探(上)
Main函数部分

Vertx-Web初探(上)

TroubleShoot部分

  • 因为启用了Cluster集群模式,所以不能直接使用factory.vertx()方法来创建Vert.x实例,如果在Cluster模式运行上边代码会遇到下边的错误信息:

Vertx-Web初探(上)
将配置文件中的集群模式先关闭

Vertx-Web初探(上)

  • 在运行的时候有一个和Performance的依赖库相关的jar,如果没有放在类路径中,则会有下边的调试日志输出:

Vertx-Web初探(上)
pom.xml文件中添加下边依赖库则可以除去这段警告信息:

Vertx-Web初探(上)
配置好过后就会输出下边合法的DEBUG信息了

Vertx-Web初探(上)

浏览器测试

运行了主函数过后,该函数并不会退出【在Eclipse中没有<terminated>标记】,先从浏览器中打开http://localhost:8080可以看到下边界面:

Vertx-Web初探(上)

针对这个简单的单实例应用的例子,简单分析一下:

  • Verticle被发布的时候,它的start()方法会被调用,注:所有的Verticle实例在Deploy时启动阶段是异步操作,看看deployVerticle方法的源代码就可以知道,只是将CompletionHandler参数设置成null的调用流程:

Vertx-Web初探(上)
记得区分VerticleFactoryVertxFactory属于不同的对象,注意下边的这段代码:

Vertx-Web初探(上)

  • 这段代码位于DeploymentManager中,这里遵循下边的原则:

    • 如果发布Worker类型的Verticle时,VerticleFactory实际上在底层会以同步的方式做Deploy操作——executeBlocking操作,它会取代event-loop线程,这个时候blockingCreate()的返回值是true,并且生成的线程名会有所区别;

    • 如果发布的不是worker类型,则默认的Verticle会使用当前的event-loop线程,这种时候blockingCreate()方法会返回false

    • 我们在真实项目中使用了两种StandardVerticle,一个用于前端Web UI,一个用于Rest接口,后端使用了四种Worker,从Deploy的速度上可以验证上边两点,当调用了deployVerticle方法过后,如果将Standard的实例数量设置得和Worker数量一致,则StandardVerticle发布和启动速度比Worker快,实例数量大的时候这个延迟会很明显;

  • start中添加线程名称打印代码,可得到下边的输出信息,从后缀的数字可以知道这些实例的Deploy是异步的:

Vertx-Web初探(上)

  • Handler内部代码属于单线程操作,如果从浏览器访问,后台会输出下边信息:

    Vertx-Web初探(上)

    这里的打印语句执行了两次是因为HttpServer默认有一个favicon的请求,实际上是两次GET请求,添加path信息就可以得到下边输出:

    Vertx-Web初探(上)

  • 同一个类型的Verticle实例可共享端口,如同上边的代码演示的,10Verticle实例共享了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的配置数据:

Vertx-Web初探(上)

上边的代码在VerticleDeploy过程会抛出下边的警告信息:

Vertx-Web初探(上)
为了解决这个问题,将代码做少许改动即可【把数据库访问部分的代码放到executeBlocking内】:

Vertx-Web初探(上)
注意区分Handler内部的代码和Verticlestart()方法的代码:

Vertx-Web初探(上)
上边高亮部分的代码的写法是JDK 8中的Lambda表达式,实际上属于代码定义,在start()方法调用的时候它并不会执行,而是在真正发送请求的时候执行;如果不熟悉Lambda表达式则可使用下边的代码写法,其效果和Demo中的效果是一致的:

Vertx-Web初探(上)
Handler部分的内容后边会提及到,这里暂时先不管,包括Handler必须实现的接口以及返回类型等,都可以不使用JDK 8的语法去实现,但Vert.x本身使用了很多Lambda的语法,所以Vert.x的最低JDK版本要求是8.0,最后需要说明的就是StandardVerticle可以不调用server.listen()方法,即不监听任何端口,这样主程序也不会退出,只是无法从浏览器访问而已,Worker类型的Verticle会经常使用这种方式来进行部署。

2.6.Cluster集群模式运行

上边一个章节所有的代码演示了非集群模式的运行代码,接下来看看集群模式的主函数怎么写,集群模式和单实例模式唯一的不同是在代码中需要提供一个ClusterManager,而Vert.x默认使用了HazelcastClusterManager实现集群管理。首先在pom.xml文件中添加下边的XML片段:

Vertx-Web初探(上)

因为项目里使用了dependencyManagement节点统一管理使用的版本信息,所以这里没有截version节点的版本信息【3.2.0】,如果运行时有问题或者IDE报错请自己加上<version>节点信息,可参考Maven资料补充上边截图片断。

添加好了上边的依赖库信息后,就可以使用HazelcastClusterManager了,看看改动过后的主函数代码:

Vertx-Web初探(上)
上边代码运行后,可直接在Console中看到Hazelcast的输出信息【这个信息在单实例运行中不会输出】如下,日志级别可以通过在资源路径(src/main/resources)中提供日志配置文件进行调整:

Vertx-Web初探(上)

  • 日志文件名:vertx-default-jul-logging.properties

  • 文件片段

Vertx-Web初探(上)

发布成功过后,和单实例一样可以通过浏览器访问了,测试部分这里就不再重复,端口依然是8080——也就是Server监听的那个端口。

额外测试

针对Cluster可以执行另外的测试,上边的输出可以看到目前只有一个节点Member在环境中,因为Hazelcast是广播的方式,所以可以从后台启动另外一个Hazelcast实例,并且启动Hazelcast的后台Console【参考Hazelcast的官方文档】,运行好过后就可以看到下边的输出信息,Member直接变成了两个,这样就启动了两个Hazelcast实例:

Vertx-Web初探(上)
并且在Console中可使用Hazelcast命令监控集群环境中的数据【上边截图的Console运行节点和下边截图的Console运行节点不一样,注意this的位置】:

Vertx-Web初探(上)

使用代码方式启动了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初探(上)的主要内容,如果未能解决你的问题,请参考以下文章

shader开发_4.Surface shader初探

iOS 初探代码混淆(OC)

unity 3D c#初探脚本之基本方法

PE病毒初探——向exe注入代码

基于深度学习的语义分割初探FCN以及pytorch代码实现

javascript 函数初探