需求: vertx cluster 依靠ignite 网络来搭建集群,不使用hazelcast 或者redis。
分析需求: 这个需求很简单,就是使用ignite搭建一个集群网络,但是由于再搭建的过程中,遇到过一些坑,记忆中好久也没有解决(在不同的ECS 上,并不是再本地),后来无意中配置下网络就可以了。
前提:
1. 首先得了解 vertx cluster 集群模式,特别集群下的eventbus
2. 需要理解 ingite 在vertx中的作用, 是如何搭建成p2p网络的
3. 另外顺便了解下,不使用hazelcast 是因为 hazelcast 社区版本耗JVM的内存,会被JVM GC统一管理,而ignite是耗堆外内存,不会收到 JVM GC的影响,另外hazelcast 专业版本也是耗堆外内存,但是是收费的。
那下面看看我们怎么连接集群网络:
第一步: 添加依赖 <dependency><groupId>io.vertx</groupId><artifactId>vertx-ignite</artifactId><version>3.5.4</dependency>
第二步:配置相关的Beans,我们特别要注意使用ignite的TcpDiscoveryVmIpFinder 网络,不要使用TcpDiscoveryMulticastIpFinder,因为会出现奇怪的问题,又不会报错,比如,在集群中,一个vertx instance 挂掉会影响其他vertx instance的通信。(eventbus 信息丢失等)。我是使用springboot configuration来配置相关的beans,请看下面的代码
@Bean
public IgniteConfigurationgetIgniteSelfConfiguration()throws Exception{
IgniteConfiguration igniteConfiguration =new IgniteConfiguration();
igniteConfiguration.setDiscoverySpi(getTcpDiscoverySpi());
igniteConfiguration.setCacheConfiguration(getCacheConfiguration());
return igniteConfiguration;
}
@Bean
public TcpDiscoverySpigetTcpDiscoverySpi()throws Exception{
TcpDiscoverySpi tcpDiscoverySpi =new TcpDiscoverySpi();
tcpDiscoverySpi.setIpFinder(getTcpDiscoveryVmIpFinder());
return tcpDiscoverySpi;
}
@Bean
public TcpDiscoveryVmIpFindergetTcpDiscoveryVmIpFinder(){
TcpDiscoveryVmIpFinder tcpDiscoveryVmIpFinder =new TcpDiscoveryVmIpFinder(); tcpDiscoveryVmIpFinder.setAddresses(Arrays.asList("10.168.128.229:47500..47509,10.168.128.208:47500..47509".split(CommonConstants.CHARACTER_SEPARATOR_COMMA)));
return tcpDiscoveryVmIpFinder;
}
@Bean
public CacheConfigurationgetCacheConfiguration(){
CacheConfiguration cacheConfiguration =new CacheConfiguration();
cacheConfiguration.setName("*");
cacheConfiguration.setCacheMode(CacheMode.PARTITIONED);
cacheConfiguration.setReadFromBackup(false); cacheConfiguration.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
cacheConfiguration.setBackups(1);
return cacheConfiguration;
}
请大家特别关注这个配置tcpDiscoveryVmIpFinder.setAddresses(), 我设置的是两个IP,和端口号,由于我们使用的是ignite (p2p gossip 协议), 那我们在使用的时候,最好能够至少指定三个iP地址和端口号。不需要把所有的节点的IP都加入进来,在集群中的节点,节点加入集群先找配置好的节点,如果存活,会将这个节点加入到集群网络中来。(大家事前请好好看看gossip 协议)。
第三步:我们来配置vertx集群模式
ClusterManager clusterManager =new IgniteClusterManager(igniteSelfConfiguration);
VertxOptions vertxOptions =new VertxOptions().setClustered(true).setClusterHost().setClusterPort().setClusterManager(clusterManager);
Vertx.clusteredVertx(vertxOptions, ar->{
});
然后就可以启动了,首先呢,我们需要注意两点:
第一点:这里没有配置EventBusOptions,我们不要在evenbusoption里面配置 clusterhost和port这类的东西,vertx instance 启动成功之后,VertxUtil.getVertxInstance().eventBus()就可以拿到集群的模式下event bus instance,如果配置了,可能eventbus 之间的通信会一直存在问题,连接refused
第二点:我们需要在vertxOption里面显示的去设置setClusterHost 和 setClusterPort,这个Ip地址和port就是event bus 通信的 ip和端口号,显示的指明之后,eventbus 通信就不会报错。这里再贴一下 setClusterHost(“IP”), 这里的Ip 是如何获取的。
public static StringgetLocalIp()throws Exception {
String localIp=null;
InetAddress inetAddress=getLocalHostLANAddress();
if(null!=inetAddress) {
//localIp = inetAddress.getHostAddress();
localIp=InetAddress.getLocalHost().getHostAddress();
}
log.info("localIP={}", localIp);
return localIp;
}
private static InetAddressgetLocalHostLANAddress()throws Exception {
InetAddress candidateAddress =null;
for (Enumeration ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); ) {
NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) {
InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();
if (!inetAddr.isLoopbackAddress()) {// 排除loopback类型地址
if (inetAddr.isSiteLocalAddress()) {
return inetAddr;
}else if (candidateAddress ==null) {
candidateAddress = inetAddr;
}
}
}
}
if (candidateAddress !=null) {
return candidateAddress;
}
InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
return jdkSuppliedAddress;
}
获取到正确的iP 之后,就可以尝试 evenbus通信了,这个时候就不会有问题了。
但是当我们想让vertx ecs cluster 集群 和 docker 节点之间通信,那么我们应该如何配置呢?我是按照下面的方式配置,结果是正确的(折腾好长时间,一直是网络通信的问题)
第一: docker-compose中填写下面的信息
vertx-instal3:
build: ./docker
hostname: ech-10-168-128-77
privileged: true
ports:
- "12001:12001"
- "12002:12002"
- "41000-47000:41000-47000" (这个端口号映射最好映射全点,vertx里面会有很多端口号,我没有仔细搞搞这个端口号,缺少和主机端口号映射的话,会导致网络通信问题)
第二步:配置Dockerfile 文件,dockerfile文件的内容如下:
FROM java:8
ENV VERTICLE_FILE vertx-network-communication-1.0-SNAPSHOT.jar
# Set the location of the verticles
ENV VERTICLE_HOME /usr/verticles
#EXPOSE 12002 12002
#EXPOSE 12001 12001
#EXPOSE 47500
#Copy your fat jar to the container
COPY $VERTICLE_FILE $VERTICLE_HOME/
#Launch the verticle
WORKDIR $VERTICLE_HOME
ENTRYPOINT ["sh", "-c"]
CMD ["java -jar $VERTICLE_FILE -cluster -cluster-host 10.168.128.77"]
最后执行docker-compose build / up 的命令就可以了。 请记住,根据gossip 协议,p2p 的网络,docker 这个节点,连接cluster 时候,配置的IP 也是 上面提到的IP,不需要改变。只要有一个可以连接通,docker这个节点就会加入到这个集群。
好了,我把ecs 和docker 集群网络配置好了, vertx 就显得简单很多,按照异步框架写代码就可以了。
关于vertx evenbus local 和cluster 模式的分析,请看很下面的博客,分析的很详细:
https://blog.csdn.net/sdmjhca/article/details/78612792
https://www.sczyh30.com/posts/Vert-x/vertx-advanced-clustered-event-bus-internal/