1 引入集群管理器

vertx http使用zookeeper集群很简单,只需要配置好pom中的dependency,然后改变一下vertx的创建方式即可。
关于集群配置的参数,在io.vertx.spi.cluster.zookeeper.ZookeeperClusterManager的源码中可以轻松找到。

1.1 聪明的vertx实例

vertx的SessionStore会自己根据vertx的是否处于分布式环境创建对应的session存储,所以不管是否是集群模式,所有的session处理可以采用同样的代码:

  1. router.route().handler(
  2. SessionHandler.create(SessionStore.create(vertx))
  3. );

通过vertx实例自动决定子组件实例的类型,这一点在vertx中有多处。

1.2 同步异步之矛盾

需要注意的是,如果你需要使用post请求,那么通常需要给route增加一个BodyHandler,这个BodyHandler需要放在SessionStore之前,因为BodyHandler如果之前有过异步操作,写法必须是:

  1. router.route().handler(routingContext -> {
  2. HttpServerRequestrequest = routingContext.request();
  3. // Pause the
  4. requestrequest.pause();
  5. someAsyncCall(result -> {// Resume the request
  6. request.resume();// And continue processing
  7. routingContext.next();
  8. });
  9. });

详见官方文档
而集群环境下的SessionStore操作中恰好有异步操作代码,所以必须修改或者简单处理,将post请求中的BodyHandler前置。否则就会报错:java.lang.IllegalStateException: Request has already been read

1.3 捣蛋的EventBus

集群化Vertx之后还有一个问题,就是EventBus也跟着集群化了,部署后底层会不停的去ping公共的EventBus服务器地址,但这个地址其实不存在,查代码得知这个公共的服务器地址默认是localhost,端口如果不设置那就是随机端口,所以在获取集群上的vertx实例时,需要设定一个开着端口的地址,必须所有的从这个集群上获取的实例都指向这个地址:

  1. var options =new VertxOptions()
  2. .setClusterManager(mgr)
  3. .setEventBusOptions(new EventBusOptions()
  4. .setClusterPublicHost("192.168.1.200")
  5. .setClusterPublicPort(23333)
  6. );

这样就不会报错了。但这样并不会让这个vertx实例发布的消息可以在远端执行,消息还是只能在实例内执行,这是一个坑。

2 存储运行时数据

vertx实例有个SharedData,里面提供了运行时数据的存取功能,可惜的是,这些数据并不能持久化,实例重启后就丢失了,单机版的session就是很好的例子。但是在集群模式下,所有的运行时数据都被传送到集群中,集群能够保存数据的话,那么SharedData可就能发挥更大的作用了。
以zookeeper集群上的vertx为例,集群管理器ZookeeperClusterManager提供了和SharedData相似的数据容器:getSyncMap,getAsyncMap,getAsyncMultiMap,后两者是异步操作,实际使用中,经常是已经在vertx的异步操作中使用这些数据容器,已然不能再进行异步操作了(还记得前面说的同步异步之间的矛盾?),所以还是同步容器用得划算。

  1. var mgr = new ZookeeperClusterManager();
  2. public static Map<Object, Object> sZKSYNCMAP = null;
  3. /*获取到vertx实例后*/
  4. sZKSYNCMAP = mgr.getSyncMap(HttpServerInstance.PROJECT_NAME);//提供多个map,每个map一个名称

此后就可以像普通map一样操作了。
需要注意的是MapEntity的定义,貌似只能用Object,不能用其他类型,之后获取之后强转,如下:

  1. eventList = JsonParser.parseString(map.get(key).toString()).getAsJsonArray();

而此后,在zookeeper集群中就能看到数据:
使用zookeeper集群 - 图1

3 通过读写数据实现消息机制

ZookeeperClusterManager提供了一个方法,getCuratorFramework可以把它包含的zookeeper客户端暴露出来给其他对象使用,但有一点,这个客户端的操作根路径是”/io.vertx”,所有的操作路径都会自动加这个前缀,所以考虑到消息消费者可能被其他角色实现,还是需要自己创建另一个客户端连接。
除了上文所说的不能发送到其他实例中去执行的问题之外,使用vertx自带的EventBus机制还有一个问题就是消息不能缓冲,然后批量执行。