Event Bus(事件总线) 是Vert.x的神经系统,负责应用系统消息的传递。Vert.x各模块(Verticle)之间的相互调用就是通过Event Bus实现的,因此各Verticle之间是高度解耦的。
Event Bus提供发布订阅功能和点对点的消息服务,类似于消息队列,每条消息在Event Bus上都有一个地址(address),发布者向这个地址发送消息,接收者从这个地址接收消息。
Event Bus独立于应用系统,它使用TCP协议进行通信。因此,在任何的应用中,只要能够创建TCP连接,都可以通过Event Bus连接到Vert.x实例,如下图所示。也正是因为这个,Vert.x天生就对分布式支持非常好。
Event Bus的api使用非常简单,下面我们分别从获取Event Bus实例,发布消息,订阅消息以及服务调用来逐一说明。
1. 获取Event Bus实例
获取Event Bus实例非常简单,直接通过Vert.x实例获取,代码如下
EventBus eb = vertx.eventBus();
可以看到,通过Vert.x实例获取Event Bus的实例和获取文件系统fileSystem以及创建HTTP服务createHttpServer非常类似。拿到Event Bus的实例之后,发现Event Bus和Vertx一样,都是接口,查看接口的代码可以非常清楚的看到Event Bus对外提供的API,代码如下
@Fluent
EventBus send(String address, Object message);
@Fluent
EventBus publish(String address, Object message);
<T> MessageConsumer<T> consumer(String address);
为了节约篇幅,去掉了部分重载的方法。主要的几个方法send,publish,consumer,sender,publisher通过方法的名字就可以猜测出方法的用途,后面根据具体的场景来分别对这些方法进行说明。
2.发布消息
发布消息的方法有多个方法的重载,最简单的发布方法接收一个地址,和一个Object类型的消息对象,如下所示
@Fluent
EventBus publish(String address, Object message);
这种方式发布的消息,所有订阅到address的消费者都能够收到消息,这也就是发布-订阅模式。还有一种是点对点模式,这种模式发布的消息只能有一个消费者收到,方法如下
@Fluent
<T> EventBus send(String address, Object message,
DeliveryOptions options, Handler<AsyncResult<Message<T>>> replyHandler);
3.订阅消息
对于订阅消息比较简单,只有一个方法,就是consumer,方法的声明如下
<T> MessageConsumer<T> consumer(String address, Handler<Message<T>> handler);
如果想要通过其他的开发语言或者在其他应用中调用Vert.x实例模块,可以通过发起TCP请求,连接到EventBus,Event Bus使用的通信协议如下,但一定不要忘记,需要创建Event Bus Bride。
<Length: uInt32><{
type: String,
address: String,
(replyAddress: String)?,
headers: JsonObject,
body: JsonObject
}: JsonObject>
4.远程服务调用
最后,附一个使用Event Bus进行远程组件调用的Demo,这里例子相对来讲可能有些复杂,但相比较真实企业项目还差的远。这个例子就是一个简单的微服务,多个服务通过Event Bus进行通信。
我们把每个Verticle单独为一个Maven模块,这个模块中核心有三个类,一个Verticle,一个对外暴露的接口Interface,一个接口的具体实现类。为了简单,这里只有两个模块。
1.第一个Verticle是对外提供服务的,暂且称之为FirstVerticle,对外提供一个sayHello服务,模块结构如下
(1)FirstVerticle中就是发布Service到总线上,代码非常简单,这里只列出start方法的代码
@Override
public void start() throws Exception {
Service service = Service.create(vertx);
new ServiceBinder(vertx).setAddress(address).register(Service.class, service);
}
发布服务,并没有调用consumer方法,而是调用了ServiceBinder类的register方法,实际上这个方法的底层就是在调用consumer方法,只是帮我们做了很多的其实事情,这里暂且不去关注。
(2)Service接口中定义sayHello方法,并且提供了获取当前实例的方法(JDK8的新特性)
@ProxyGen
@VertxGen
public interface Service {
/**
* 这个方法是提供给自己使用,方便创建服务的实现类
*
* @param vertx
* @return
*/
static Service create(Vertx vertx) {
return new ServiceImpl(vertx);
}
/**
* 这个方法给消费者使用,便于消费者创建生产者的代理类,以此来消费生产者的服务
*
* @param vertx
* @param address
* @return
*/
static Service createProxy(Vertx vertx, String address) {
return new ServiceVertxEBProxy(vertx, address);
}
/**
* 测试接口方法
*
* @param name
*/
void sayHello(String name, Handler<AsyncResult<JsonObject>> resultHandle);
}
这里有三个方法,create方法是为了便于获得类的实例,这个比较好理解。createProxy方法实际上在我们当前的演示案例中并没有使用到,这里读者可以先不用去关注。对于sayHello方法的resultHandle这个参数可能不是很理解,这个参数非常简单,因为Vert.x是一个异步框架,sayHello方法也是异步执行的,那么方法的返回结构就是通过这个参数封装的。
(3)ServiceImpl的实现类的代码就更简单了,就一个sayHello方法的实现,如下
@Override
public void sayHello(String name, Handler<AsyncResult<JsonObject>> resultHandle) {
resultHandle.handle(Future.succeededFuture(new JsonObject().put("msg", "SUCCESS")));
}
给调用者回了个Json对象,这个对象中包含一个msg,值为SUCCESS
(4)还有四,不就上面三个吗,还有一个很关键的部分,package-info
@ModuleGen(groupPackage = "stu.vertx.cluster.service.hello", name = "FirstVerticle")
package stu.vertx.cluster.service.hello;
import io.vertx.codegen.annotations.ModuleGen;
这里描述的是这个模块的包,这个是必须的,否则ServiceProxy不能生成,其他的也都是白扯。所以这里,很关键,很关键,很关键。
2.第一个组件就完成了,下面是第二个组件,第二个组件来调用第一个组件,完成一系列操作。这个组件接收客户端请求,然后接收客户端上送的name属性,传递给第二个组件,并拿到第二个组件的返回值。这个组件不对外提供服务,因此就一个Verticle就可以了。代码如下
@Override
public void start() throws Exception {
HttpServer httpServer = vertx.createHttpServer();
httpServer.requestHandler(request -> {
// 获取到response对象
HttpServerResponse response = request.response();
// 设置响应头
response.putHeader("Content-type", "text/html;charset=utf-8");
// 通过配置action参数,指定要走哪一个方法
DeliveryOptions options = new DeliveryOptions();
options.addHeader("action", "sayHello");
// 这个是给方法传入的参数
JsonObject config = new JsonObject();
config.put("name", "xiaozhang");
// 通过eventBus调用方法
vertx.eventBus().<JsonObject>send("service.demo.firstverticle", config, options, res -> {
// 响应数据
response.end(res.result().body().getString("msg"));
});
});
httpServer.listen(1234);
}
OK,到这里,一个远程服务调用就完成了,是不是非常简单!
(一)Vert.x 简明介绍 Vert.x(vertx) 简明介绍_jhappyfly的博客-CSDN博客
(二)Vert.x创建简单的HTTP服务 Vert.x(vertx) 创建HTTP服务_jhappyfly的博客-CSDN博客
(三)Vert.x Web开发之路由 Vert.x(vertx) Web开发-路由_vert.x web开发_jhappyfly的博客-CSDN博客
(四)Vert.x TCP服务实现 Vert.x(vertx) 实现TCP服务_vertx怎么在任意地方写数据到tcp连接_jhappyfly的博客-CSDN博客
(五)Vert.x数据库访问 Vert.x(vertx) 连接MySQL、Oracle数据库_vertx mybatis_jhappyfly的博客-CSDN博客
(六)Vert.x认证和授权 Vert.x(vertx) 认证和授权详解(包含认证和授权在Web系统中的使用)_vertx auth_jhappyfly的博客-CSDN博客
(七)Vert.x事件总线(Event Bus)与远程服务调用 Vert.x(vertx) 事件总线(EventBus)与 远程服务调用_vertx.eventbus_jhappyfly的博客-CSDN博客
Vert.x 案例代码:https://github.com/happy-fly