Vert.x(vertx) 事件总线(EventBus)与 远程服务调用

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天生就对分布式支持非常好。

Vert.x(vertx) 事件总线(EventBus)与 远程服务调用_第1张图片

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);

 MessageConsumer consumer(String address);

为了节约篇幅,去掉了部分重载的方法。主要的几个方法send,publish,consumer,sender,publisher通过方法的名字就可以猜测出方法的用途,后面根据具体的场景来分别对这些方法进行说明。

2.发布消息

发布消息的方法有多个方法的重载,最简单的发布方法接收一个地址,和一个Object类型的消息对象,如下所示

@Fluent
EventBus publish(String address, Object message);

这种方式发布的消息,所有订阅到address的消费者都能够收到消息,这也就是发布-订阅模式。还有一种是点对点模式,这种模式发布的消息只能有一个消费者收到,方法如下

@Fluent
 EventBus send(String address, Object message,
 DeliveryOptions options, Handler>> replyHandler);

3.订阅消息

对于订阅消息比较简单,只有一个方法,就是consumer,方法的声明如下

 MessageConsumer consumer(String address, Handler> handler);

如果想要通过其他的开发语言或者在其他应用中调用Vert.x实例模块,可以通过发起TCP请求,连接到EventBus,Event Bus使用的通信协议如下,但一定不要忘记,需要创建Event Bus Bride。

<{

  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> resultHandle);

}

这里有三个方法,create方法是为了便于获得类的实例,这个比较好理解。createProxy方法实际上在我们当前的演示案例中并没有使用到,这里读者可以先不用去关注。对于sayHello方法的resultHandle这个参数可能不是很理解,这个参数非常简单,因为Vert.x是一个异步框架,sayHello方法也是异步执行的,那么方法的返回结构就是通过这个参数封装的。

(3)ServiceImpl的实现类的代码就更简单了,就一个sayHello方法的实现,如下

@Override
public void sayHello(String name, Handler> 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().send("service.demo.firstverticle", config, options, res -> {
			// 响应数据
			response.end(res.result().body().getString("msg"));
		});

	});

	httpServer.listen(1234);
}

OK,到这里,一个远程服务调用就完成了,是不是非常简单!

 

(一)Vert.x 简明介绍 https://blog.csdn.net/king_kgh/article/details/80772657

(二)Vert.x创建简单的HTTP服务 https://blog.csdn.net/king_kgh/article/details/80804078

(三)Vert.x Web开发之路由 https://blog.csdn.net/king_kgh/article/details/80848571

(四)Vert.x TCP服务实现 https://blog.csdn.net/king_kgh/article/details/84870775

(五)Vert.x数据库访问 https://blog.csdn.net/king_kgh/article/details/84894599

(六)Vert.x认证和授权 https://blog.csdn.net/king_kgh/article/details/85218454

(七)Vert.x事件总线(Event Bus)与远程服务调用 https://blog.csdn.net/king_kgh/article/details/86993812

Vert.x 案例代码:https://github.com/happy-fly

 

笔者就职于一家互联网支付公司,公司的核心项目使用的是Vert.x技术体系。记得笔者刚进入公司,接触Vert.x的时候,找遍了大大小小的网站,发现市面上关于Vert.x的文档除了官方文档外,几乎找不到其他资料。当时就励志要出一个专栏来写写Vert.x,以此帮助在Vert.x上采坑的朋友。因为笔者能力有限,文章中难免会有错误和疏漏,如果您对文章有意见或者好的建议,可以直接留言或者发送邮件到[email protected]。如果您也对Vert.感兴趣,可以加入到我们,共同学习Vert.x,并推进国内开发者对Vert.x的认知。

你可能感兴趣的:(Java)