My Avatar

Shadow

I love bleak day, like something will happen

Vertx

2018年03月22日 星期四, 发表于 北京

如果你对本文有任何的建议或者疑问, 可以在 这里给我提 Issues, 谢谢! :)

  这几年做的项目都是基于Vertx,那么Vertx究竟是个什么东西呢?


Vertx概述

  vertx官网上,作者用一句话概括了vertx:JVM上的reactive应用开发套件

  那么究竟vertx是什么东西呢?

  vertx包含了一些不同的组件,这些组件是用来帮助你更方便地构建多语言类型的reactive应用的。

  vert.x是高度模块化的,你可以就用你需要的那部分,而不用其他的。

  而且,vertx只是一个库,在java中,你可以把他想成就是一堆jar包,而并不是一个限制性的容器,所以你没有必要只用vertx提供的组件,你可以连同其他任何你想使用的库和vertx一起使用。

  异步无阻塞、事件驱动、消息驱动、reactive、依赖少

reactor pattern

  vertx是遵循reactor pattern的,那么何谓reactor pattern呢?

  在维基百科上,是这样解释的:

  reactor模式是一种事件处理模式,用来处理很多并发的服务请求。将进来的请求同步分配到相关的请求处理器上处理。

  reactor模式有四个元素:

  第一种:资源(Resources)

  所谓资源,就是任何能提供输入或者能从系统接收输出的,例如数据库、http请求等等

  第二种:event loop(Demultiplexer)

  一般event loop都是一个单线程,对任何资源的操作都是在event loop上,因此在操作某个资源时,eventloop是阻塞的,并不能对其他资源进行处理,但是event loop只会在某个资源准备好,能够非阻塞进行处理时,才会将他分配给一个分配器去处理(例如,执行read操作时,如果没有数据,则会阻塞住,但是reactor模式下,在数据没有准备好时,eventloop并不会阻塞在这,只有当数据准备好了,eventloop才会将数据分配到dispatcher去处理)

  第三种:分配器(Dispatcher)

  所谓dispatcher就是负责处理请求处理器的注册和释放的,他会负责将资源从Demultiplexer上分发到相应的请求处理器上

  第四种:请求处理器(Request Handler)

  由各自应用去定义自己的请求处理器以及对应的资源,这样当资源准备好后,应用的请求处理器会按照定义好的逻辑去处理资源

  而vertx就是明显这样的一个reactor模式,我们来看下vertx几个核心特性:

  1. Don’t call us, we’ll call you.(事件驱动)

  vertx里面的api大部分都是事件驱动型,这意味着,当你感兴趣的事情发生时,vertx会通过发送事件告诉你。而在实现上,通常是通过注册一个handler,来异步第进行处理。

  2. Dont’t block me (非阻塞)

  除了一些文件的同步操作,vertx里的几乎所有api都是非阻塞的,如果结果能很快提供,那么就直接返回,如果不能,则通常提供一个handler来接受处理的结果,同时返回,这时并没有阻塞线程。而那个费时的操作是丢给worker线程去处理了,处理完将结果发送到handler。

  正是因为这个非阻塞的特性,vertx能利用少量的线程处理大量并发的请求,我记得那时候我开发网关的时候,做过压测,单实例TPS能达到5000

  3. Reactor and Multi-Reactor (多reactor模式)

  之前提到了vertx是事件驱动型,他会将事件送到你的handler中去处理,而调用handler的线程就被称为event loop,所谓的非阻塞也就是指event loop线程不会被阻塞,他永远只负责将各种各样的事件传送到不同的handler中。

  而这种模式正是我们前面提到过的reactor模式。

  NodeJS就实现了这种模式,在一个标准的reactor模式下,一般只有一个event loop线程,而这样的问题在于,一个单一的线程只能在一个核上运行,所以如果你有多个核,但是你想扩展你的单线程reactor应用,你只能是创建并管理多个这样的程序。

  vertx则不是,它维护了多个event loop。默认情况下,是核数的两倍。这样,每个核上都能运行至少一个event loop,从而不会造成资源浪费。

  另外需要注意的是,虽然有多个event loop,但是所有的handler依然不会并发地执行,他们一般都在相同的event loop上顺序执行。所以并不需要担心并发的问题

verticle

  vertx的基础就是verticle,所谓verticle就是通过vertx部署并运行的一堆代码而已。verticle可以用vertx支持的任何语言实现,而且一个应用可以包含多个用不同语言编写的verticles

  如果你了解Actor Model,那么verticle就像是里面的actor的角色,actor是一个计算的实体,他能接收很多并发的消息,并对之做出响应,同时也能创建新的actors,并且能发送消息到别的actor上

  而在vertx上,一个应用通常由多个运行在同一个vertx实例上的verticle组成,不同的verticle通过在event bus上发送消息来实现沟通交流

  vertx提供了三种不同类型的verticle,下面详细介绍每种类型的不同

  1. Standard Verticles

  标准verticle,这些是最常用也是最有用的类型,标准verticle始终都使用同一个event loop线程来执行。

  当一个标准verticle被创建的时候,他就被分配了一个evetloop,与此同时,该verticle的start方法就开始在这个eventloop上执行了。以后,当你调用任何需要handler的方法时,vertx会保证这些handler被调用时都在同一个eventloop下执行。

  也就是说,我们能保证在你的verticle实例里的所有代码都是在同一个eventloop里执行(只要你没有在verticle里面创建你自己的线程)

  而这也就意味着,你可以在你的应用里,当做一个单线程来写你所有的代码,再也不用担心同步和volatile等在传统的多线程变成环境下的各类问题了,

  2. Worker verticles      worker verticle跟标准verticle相似,不同点在于,它并不是在event loop线程上执行,而是从worker线程池里取出的线程来执行。因此他是被设计用来执行那些blocking code的,也就是如果你有些代码是肯定会导致线程阻塞的话,你不要傻夫夫的在标准verticle上执行,这样会阻塞event loop,导致其他共用该event loop的verticle也阻塞,导致性能下降,这种情况下,将这种阻塞代码部署在worker verticle上,只有它所在worker verticle会被阻塞,不会影响到其他标准verticle。

  但是你需要注意的是,worker verticle 实例永远不会被并发地执行,也就同一个代码,并不会在同一时间被多个线程执行,他永远都是单线程来执行,只不过跟event loop不同的是,他这个执行的线程是会变的,并不是分配完就固定了。他是从worker线程池中取出来的空闲线程。

  3. Multi-threaded worker verticles

  而Multi-threaded worker verticle则是跟普通的worker verticle一样,但是他能同时被多个线程并发执行。

  但是这样,你就需要在自己的代码中注意使用传统多线程的保持一致性的方法了

缺点

  1. 不能做cpu密集型计算(但实际上可以用worker verticle解决 我认为)

  2. callback hell问题

  因为vertx基于java8,而且是一个异步框架,所以很多的lamda表达式来接受异步处理结果,这样就容易导致callback hell问题,例如:

1
2
3
4
5
6
7
ar ->{
	ar1 ->{
		ar2 ->{
			......
		}
	}
}

  如果异步处理里面,又有异步处理,这样子子孙孙无穷尽也,阅读起来极不方便,调试的时候也会发现很多问题,例如变量不可见了等等

  但其实可以通过Future来解决