Vertx,融合Java、Ruby、Python等语言的高性能架构
目录:
一、Vert.x简介
二、Vert.x原理解析
三、Vert牛刀小试
四、Vert应用实践
五、辅助工具
六、名词解释
七、后记
一、Vert.x 简介
官方文档:http://vertx.io/docs/vertx-core/java/
Vertx,是一个基于JVM、轻量级、高性能的应用平台,非常适用于移动端后台、互联网、企业应用架构。
Vertx,基于Netty全异步通信,并扩展出了很多有用的特性。
Vertx,是基于事件总线设计的高性能架构,保证应用中不同部分以一种非堵塞的线程安全方式通讯。
Vertx,是借鉴Erlang和Akka架构设计,能充分利用多核处理器性能并实现高并发编程需求的框架。
Vertx特点:
支持多种编程语言
目前支持Java、JavaScript、Ruby、Python、Groovy、Clojure、Ceylon等,并提供友好的API接口。以上技术栈的工程师可以非常容易的学习和使用Vert.x 架构。
异步无锁编程
经典的多线程编程模型能满足很多Web开发场景,但随着移动互联网并发连接数的猛增,多线程并发控制模型性能难以扩展,同时要想控制好并发锁需要较高的技巧,目前Reactor异步编程模型开始跑马圈地,而Vert.x就是这种异步无锁编程的一个首选。
(注:Reactor?文末有解释)
对各种IO的丰富支持
目前Vert.x的异步模型已支持TCP、UDP、FileSystem、DNS、EventBus、Sockjs等,基本满足绝大多数系统架构需求。
分布式消息传输
Vert.x基于分布式Bus消息机制实现其Actor模型,我们的业务逻辑如果依赖其他Actor则通过Bus简单的将消息发送出去就可以了。EventBus事件总线,可以轻松编写分布式解耦的程序,具有很好的扩展性。
EventBus也是Vert.x架构的灵魂所在。
生态体系日趋成熟
Vertx归入Eclipse基金会门下,异步驱动已经支持了Postgres、MySQL、MongoDB、Redis等常用组件,并且有若干Vertx在生产环境中的应用案例。
Vertx是轻量级的
vertx的核心代码包只有650kB左右,同时提供丰富的扩展插件,满足各类需求。
Vertx并不是一个Web容器
Vertx并不是一个Web Server,它是一种异步编程框架,你可以将自己基于vert.x的应用程序放置到任何你想放置的Web容器中部署运行,可以非常方便的和Spring,Spring Boot,Spring Cloud,Nodejs等语言混编。
模块化
Vertx本身内置强大的模块管理机制,当你写完一个Vert.x业务逻辑的时候,你可以将其打包成module,然后部署到基于Maven的仓库里,与现有主流的开发过程无缝结合。
支持WebSocket
支持WebSocket协议兼容SockJS , 可以非常方便的实现web前端和服务后端长连接通信,是轻量级web聊天室应用首选解决方案。
使用简单
这里的简单意味着你编写的代码是完全基于异步事件的,类似Node.JS,与此同时.你不需要关注线程上的同步,与锁之类的概念,所有的程序都是异步执行并且通信是无阻塞的。
良好的扩展性
因为基于Actor模型,所以你的程序都是一个点一个点的单独在跑,一群点可以组成一个服务,某个点都是可以水平扩展,动态替换,这样你的程序,基本就可以达到无限制的水平扩展。
高并发性
vert.x是一个事件驱动非阻塞的异步编程框架,你可以在极少的核心线程里占用最小限度的硬件资源处理大量的高并发请求。
此并发并非JDK库的并发,当你Coding的时候,不再关注其锁,同步块,死锁之类的概念,你就可以随性所欲的写自己的业务逻辑,Vert.x本身内置三种线程池帮你处理。
目前,该框架国内用的还不太多,但是国外比较多,如英孚教育、Hulu、以及做JVM监控的一家公司jClarity等。Google group上的话题也比较多,非常活跃,属于技术极客圈里热门话题。
如果你是技术小白,可以通过学习Vertx拓宽你的技术栈;如果你是架构师,可以借鉴Vertx的架构设计思路,更好的提升自我的架构能力,也为公司未来的技术选型,多一种选择和考量。
二、Vertx原理解析
本小节主要讲一些理论性的东西,核心是想讲明白Vertx的体系结构,verticle和vertx的关系,以及verticles 之间是如何通信和运行的,很枯燥的一章。建议大家先大体看看,不用立刻理解(也不可能),带着疑问继续读后面的几个章节。
Vertx应用在很大程度上是通过定义事件处理程序。应用之间通过事件总线传递消息,异步响应。
Vertx的编程框架可以简单的理解为:
verticle是vert.x中可被部署运行的最小代码块,可以理解为一个verticle就是一个最小化的业务处理引擎。一个应用程序可以是单个verticle或者由EventBus通信的多个verticles构成。
verticle被发布部署后,会调用其内部的start方法,开始业务逻辑处理,完成后会调用stop方法,对该代码块执行销毁动作。
Verticles在Vert.x实例中运行。一个Vertx可以承载多个verticles,每个Vert.x实例在其私有的JVM实例运行。一台服务器可以运行一个或多个Vert.x实例(建议运行Vertx实例的数量和CPU核数正相关)。
一个Vert.x实例,保证其承载的verticles实例总是在同一个线程执行, 并发Vert.x 是单线程的。
在内部,Vert.x实例维护一组线程(通常为CPU核心数)是在执行一个事件循环。
虽然你可能会认为,共享数据和可扩展性截然相反。Vert.x提供了一个共享的MAP和跨在同一Vert.x实例中运行verticles传递不可改变的数据共享一套设施,这时候数据是可变的唯一真正的 。
Vert.x使用相对较少的线程来创建一个事件循环和执行verticles。但在某些情况下,需要verticle做一些要么昂贵计算,或可能阻塞,如连接到数据库。当发生这种情况Vert.x可以让你标记verticle实例作为worker verticle 。
Vert.x确保worker verticles将永远不会被同时执行。
1、Vert.x系统的体系结构
2、客户端请求处理流程
请求在vert.x框架中被插入到一个事件队列中,耗时的任务将被委托给长任务处理线程进行处理,保证线程不被耗时的IO操作进行阻塞等待。
如下图:
3、EventBus通信模型
事件循环线程和长任务处理线程通过EventBus进行通信。
如下图:
4、部分名词对照表:
handler
事件处理器
event loop
事件循环(线程)
verticle
Vert.x的专有名词。指代Vert.x中基本的功能单元,一个Vert.x应用应该是由一组verticles构成的。
worker
顾名思义,干活的(线程)。对应文档中有worker thread pool,worker verticle(Vert.x里的work verticle与标准版verticle区别较大)。
event bus
事件总线
Vert.x核心功能是最底层且相当简洁的,没有诸如数据库存取、认证、高级web等功能,你可以在Vert.x ext(扩展包)找到以上这些组件。
5、Vert.x Core提供的功能:
编写TCP客户端和服务器
编写 HTTP 客户端和服务器包括 Websocket 支持
事件总线(Event bus)
共享的数据-本地的map和分布式的map
定时和延时运行
部署和非部署 Verticles
Sockets
DNS 客户端
文件系统
高可用性
集群
三、Vertx 的牛刀小试
本节将教你如何一步一步地创建第一个vert.x应用程序。
1、基础环境:
Vertx.x支持的操作系统
Linux、OSX和Windows
JDK
Vertx.x需要JDK7以上版本,可以选择Oracle JDK或OpenJDK。
(如果使用非java版Vertx,无需JDK,只需要对应开发语言的安装包就好,本文以Java版为基础进行讲解)
2、创建vert.x应用程序
在pom.xml中增加:
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>3.2.0</version>
</dependency>
创建Verticle
Verticle实例是一个自定义的Verticle类的对象,自定义的Verticle实例需要继承AbstractVerticle类,如下所示,创建自定义 Verticle 即MyVerticle
Verticle有两个有用的方法:
start()和stop()
这两个方法在Verticle实例被注册的Vertx实例或者从Vertx实例卸载的时候被自动调用。
你可以在start()里加入一些需要做的工作,并且在stop里加入一些需要清理的工作。
注册Verticle实例到Vertx实例
Vertex对象是Vert.x的控制中心,创建客户端和服务器、获取事件总线(event bus)的引用、设置定时器等功能都需要该实例。
该Vertx实例,其实并不能做具体事情。为了让程序能做一些有用的事情,需要将自定义的MyVerticle实例注册到Vertx。
Vertx提供两种注册方法:
通过传递一个Verticle实例参数(如本例使用)
通过包名称进行注册(如本例注释部分)
测试运行效果
在IDE 中启动VertStart,控
制台输出
MyVerticle started!
到此,你已经完成了第一个 vert应用程序的创建和使用。
在下一节,我们将扩展开讲解vert的常规应用,通过这一节的学习,你将深入认识vert的开发模式。
四、Vert应用实践
上一小节,我们实现了第一个vert程序,本小节将更多更全面的展示vert的使用方法。
1、搭建Vert.x Web服务器代码示例
pom.xml文件引入web扩展包
RestfulVerticle.java
启动RestfulVerticle.java
浏览器访问
http://localhost:8080/get/1/2
或 终端执行
crul http://localhost:8080/get/1/2
返回内容:
{
'method' : 'get',
'param1' : '1',
'param2' : '2'
}
浏览器访问
http://localhost:8080/assets/
返回页面
index.html
解析:
HttpServer:
http/https/websockets服务器,vert.x发布restful服务,不需要tomcat提供servlet容器,它自己就是一台性能强大的web服务器,并且原生支持负载均衡,后面我们会讲到。
Router类:
Router类可以理解为一个路由器,他接收httpserver带来的请求,并将不同的请求分发到不同的路由中,如果简单对比一下spring mvc的话,可以将router理解为spring mvc中的dispatcher。
route:
route代表一条路由,同样,对比spring mvc,相当于spring中的@RequestMapping,他指定了restful api的请求接口路径,并将其交给handler来处理该条路由。
Handler:
handler处理具体的路由请求,字面上讲就是处理某个具体的restful api。他与httpserver,router,route的关系可以用如下流程表示:
来自httpserver的request请求-->交由路由器做分发处理-->路由器匹配到具体的路由规则-->路由到最终的handler去处理请求
vert.x默认提供了很多处理器,包括但不局限于以下:
AuthHandler 处理权限校验支持
BodyHandler 提供所有请求上下文
CookieHandler 提供cookie支持
SessionHandler 提供session支持
RoutingContext:
请求上下文,可以理解为servlet中的httprequest和httpresponse
在RestfulVerticle.java的start方法里创建了一个Router对象。
router是Vert.x Web的基础,负责分发HTTP请求到handler(处理器),在Vert.x Web中还有两个很重要的概念。
Routes-定义请求的分发
Handlers-这是实际处理请求并且返回结果的地方。Handlers可以被链接起来使用。
router::accept,它表示引用一个方法(这里是引用router的accept方法)。当接收到一个请求的时候,告诉vert.x从router里调用accept方法。
将访问“/assets/*”的请求,route到“assets”目录下的资源,默认访问index.html。
2、处理session代码示例
访问:http://localhost:8080/ 计数器 1返回。
3、处理cookies代码示例
访问:http://localhost:8080/ 计数器 1,返回。
4、Vert.x通信
为了建立一个Vert.x消息系统,需要启动vertx实例的eventBus()方法,从而获得该事件总线。
EventBus eb = vertx.eventBus();
Vertx提供两种发布消息的方式
publish()
send()
两个组件要异步地相互通信时,使用该方式。
代码示例
接收消息:
注册handlers
当收到一条消息时,handler会被调用,而消息(message)会作为参数传递过去。
当在一个集群event bus上注册了handler时,完成在集群上每个节点的注册需要花点时间。
如果你想在完成时得到通知,你可以在MessageConsumer 对象上注册一个completion handler
发布消息
eventBus.publish('news.uk.sport', 'Yay! Someone kicked a ball');
如果想点对点的发布消息即只有一个接收到消息,可以使用send方法
eventBus.send('news.uk.sport', 'Yay! Someone kicked a ball');
使用send方法时event bus会尝试把消息发送到注册在event bus上的MessageConsumer对象。
某些情况下,发送方可能想知道消息已经被接收并处理了。
为了让发放方了解消息已被处理,consumer可以通过调用reply方法给予回应。
如果这么做了,那么发送方将会收到一个回应,并且回应handler将被调用。看下面这个例子:
接收方:
发送方:
5、定期、延时执行
在标准verticle里,你不能以使线程休眠的方式引入延迟;这样干会阻塞event loop线程.
取而代之的是Vert.x定时器,定时器分为一次性(one-shot)和周期性(periodic)
为一次性定时器:
周期性:
如果你是在verticle内部创建的定时器,那么verticle被卸载时,这些定时器将被自动关闭。
(注意:定时器会定期触发。如果你的定期处理需要耗费大量时间,你的定时器事件可能会连续运行甚至糟糕到堆积在一起。在这种情况下,你应该考虑转而使用setTimer。一旦你的处理完成了,你可以再设置下一个定时器。)
五、辅助工具
Vert.x3常用工具
最后在提一下几个Vert.x3的一些小工具。
metrics
这个可以用来统计整个Vert.x内部的一些指标信息,比如HTTP请求数,TCP接受或者发送的流量等等,具体可以看官方文档,通过这个接口我们可以实时的统计Vert.x内部性能信息。
vertx-test-unit
Vert.x提供了专门针对异步代码的单元测试框架。
通过redeploy这个参数可以动态的热部署整个verticle,这个对开发调试时非常有用。
EventLoopChecker
Vert.x3内置了EventLoopChecker这个动态监测所有EventLoop线程的工具,默认EventLoop被阻塞了2秒钟的时候会触发报警,如果持续阻塞则会直接打印那一块的异常栈到日志里,非常方便开发者来检查自己的异步代码。
六、名词解释
Reactor模式
和传统Java框架的多线程模型相比,Vert.x Netty是Reactor模式的Java实现。考古了一下Reactor模式,其理论最早由Washington University的Douglas C.Schmidt教授在1995年提出,在《Proactor - An ObjectBehavioral Pattern for Demultiplexing and DispatchingHandlers for Asynchronous Events 》这篇论文中做了完整介绍。
图1-6是对其关键原理部分展开分析。
图1 一个经典Web Server在收到Web浏览器请求后的处理过程
图2 一个经典Web Server使用多线程模型,并发处理来自多个Web浏览器的请求
图3 Web浏览器连接到一个Reactor模式的Web Server处理过程。利用了Initiation Dispatcher组件,把耗时的IO操作事件注册到InitiationDispatcher组件
图4 Web浏览器访问一个Reactor模式的Web Server处理过程。耗时IO操作由其它线程执行,IO执行完成后通知Initiation Dispatcher,再回到Http Handler执行
图5 Web浏览器连接一个Proactor模式的Web Server处理过程。和Reactor的主要区别是耗时IO操作交给操作系统异步IO库执行(例如GNU/Linux aio),操作系统异步IO库执行完毕后,通过异步IO通知机制(例如epoll)触发Completion Dispatch,再交给Http Handler执行
图6 Web浏览器访问一个Proactor模式的Web Server处理过程。和Reactor的主要区别是耗时IO操作交给操作系统异步IO库执行(例如GNU/Linux aio),操作系统异步IO库执行完毕后,通过异步IO通知机制(例如epoll)触发Completion Dispatch,再交给Http Handler执行
事实上,Vert.x/Netty的Reactor实现部分是在Netty4.0如上述所示的代码中实现,和上述图中能对应的几个类是io.netty.channel.nio.NioEventLoop,io.netty.channel.epoll.EpollEventLoop,java.nio.channels.spi.SelectorProvide
七、后记
Vertx是什么,有哪些特点,以及基本使用方法,本文做了基本的介绍。
作为一个优秀的异步通信框架,Vertx还有非常多的高级应用和完整的生态体系。在后面的文章中,我会为大家持续解开Vertx的神秘面纱。
https://github.com/wzjgn/Vertx.git