1 引入集群管理器
vertx http使用zookeeper集群很简单,只需要配置好pom中的dependency,然后改变一下vertx的创建方式即可。
关于集群配置的参数,在io.vertx.spi.cluster.zookeeper.ZookeeperClusterManager的源码中可以轻松找到。
1.1 聪明的vertx实例
vertx的SessionStore会自己根据vertx的是否处于分布式环境创建对应的session存储,所以不管是否是集群模式,所有的session处理可以采用同样的代码:
router.route().handler(
SessionHandler.create(SessionStore.create(vertx))
);
通过vertx实例自动决定子组件实例的类型,这一点在vertx中有多处。
1.2 同步异步之矛盾
需要注意的是,如果你需要使用post请求,那么通常需要给route增加一个BodyHandler,这个BodyHandler需要放在SessionStore之前,因为BodyHandler如果之前有过异步操作,写法必须是:
router.route().handler(routingContext -> {
HttpServerRequestrequest = routingContext.request();
// Pause the
requestrequest.pause();
someAsyncCall(result -> {// Resume the request
request.resume();// And continue processing
routingContext.next();
});
});
详见官方文档
而集群环境下的SessionStore操作中恰好有异步操作代码,所以必须修改或者简单处理,将post请求中的BodyHandler前置。否则就会报错:java.lang.IllegalStateException: Request has already been read
1.3 捣蛋的EventBus
集群化Vertx之后还有一个问题,就是EventBus也跟着集群化了,部署后底层会不停的去ping公共的EventBus服务器地址,但这个地址其实不存在,查代码得知这个公共的服务器地址默认是localhost,端口如果不设置那就是随机端口,所以在获取集群上的vertx实例时,需要设定一个开着端口的地址,必须所有的从这个集群上获取的实例都指向这个地址:
var options =new VertxOptions()
.setClusterManager(mgr)
.setEventBusOptions(new EventBusOptions()
.setClusterPublicHost("192.168.1.200")
.setClusterPublicPort(23333)
);
这样就不会报错了。但这样并不会让这个vertx实例发布的消息可以在远端执行,消息还是只能在实例内执行,这是一个坑。
2 存储运行时数据
vertx实例有个SharedData,里面提供了运行时数据的存取功能,可惜的是,这些数据并不能持久化,实例重启后就丢失了,单机版的session就是很好的例子。但是在集群模式下,所有的运行时数据都被传送到集群中,集群能够保存数据的话,那么SharedData可就能发挥更大的作用了。
以zookeeper集群上的vertx为例,集群管理器ZookeeperClusterManager提供了和SharedData相似的数据容器:getSyncMap,getAsyncMap,getAsyncMultiMap,后两者是异步操作,实际使用中,经常是已经在vertx的异步操作中使用这些数据容器,已然不能再进行异步操作了(还记得前面说的同步异步之间的矛盾?),所以还是同步容器用得划算。
var mgr = new ZookeeperClusterManager();
public static Map<Object, Object> sZKSYNCMAP = null;
/*获取到vertx实例后*/
sZKSYNCMAP = mgr.getSyncMap(HttpServerInstance.PROJECT_NAME);//提供多个map,每个map一个名称
此后就可以像普通map一样操作了。
需要注意的是MapEntity的定义,貌似只能用Object,不能用其他类型,之后获取之后强转,如下:
eventList = JsonParser.parseString(map.get(key).toString()).getAsJsonArray();
3 通过读写数据实现消息机制
ZookeeperClusterManager提供了一个方法,getCuratorFramework可以把它包含的zookeeper客户端暴露出来给其他对象使用,但有一点,这个客户端的操作根路径是”/io.vertx”,所有的操作路径都会自动加这个前缀,所以考虑到消息消费者可能被其他角色实现,还是需要自己创建另一个客户端连接。
除了上文所说的不能发送到其他实例中去执行的问题之外,使用vertx自带的EventBus机制还有一个问题就是消息不能缓冲,然后批量执行。