需求: vertx cluster 依靠ignite 網(wǎng)絡(luò)來搭建集群,不使用hazelcast 或者redis。
分析需求: 這個(gè)需求很簡單,就是使用ignite搭建一個(gè)集群網(wǎng)絡(luò),但是由于再搭建的過程中,遇到過一些坑,記憶中好久也沒有解決(在不同的ECS 上,并不是再本地),后來無意中配置下網(wǎng)絡(luò)就可以了。
前提:
1. 首先得了解 vertx cluster 集群模式,特別集群下的eventbus
2. 需要理解 ingite 在vertx中的作用, 是如何搭建成p2p網(wǎng)絡(luò)的
3. 另外順便了解下,不使用hazelcast 是因?yàn)?hazelcast 社區(qū)版本耗JVM的內(nèi)存,會被JVM GC統(tǒng)一管理,而ignite是耗堆外內(nèi)存,不會收到 JVM GC的影響,另外hazelcast 專業(yè)版本也是耗堆外內(nèi)存,但是是收費(fèi)的。
那下面看看我們怎么連接集群網(wǎng)絡(luò):
第一步: 添加依賴? ? <dependency><groupId>io.vertx</groupId><artifactId>vertx-ignite</artifactId><version>3.5.4</dependency>
第二步:配置相關(guān)的Beans,我們特別要注意使用ignite的TcpDiscoveryVmIpFinder 網(wǎng)絡(luò),不要使用TcpDiscoveryMulticastIpFinder,因?yàn)闀霈F(xiàn)奇怪的問題,又不會報(bào)錯(cuò),比如,在集群中,一個(gè)vertx instance 掛掉會影響其他vertx instance的通信。(eventbus 信息丟失等)。我是使用springboot configuration來配置相關(guān)的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;
}
請大家特別關(guān)注這個(gè)配置tcpDiscoveryVmIpFinder.setAddresses(), 我設(shè)置的是兩個(gè)IP,和端口號,由于我們使用的是ignite (p2p gossip 協(xié)議), 那我們在使用的時(shí)候,最好能夠至少指定三個(gè)iP地址和端口號。不需要把所有的節(jié)點(diǎn)的IP都加入進(jìn)來,在集群中的節(jié)點(diǎn),節(jié)點(diǎn)加入集群先找配置好的節(jié)點(diǎn),如果存活,會將這個(gè)節(jié)點(diǎn)加入到集群網(wǎng)絡(luò)中來。(大家事前請好好看看gossip 協(xié)議)。
第三步:我們來配置vertx集群模式
ClusterManager clusterManager =new IgniteClusterManager(igniteSelfConfiguration);
VertxOptions vertxOptions =new VertxOptions().setClustered(true).setClusterHost().setClusterPort().setClusterManager(clusterManager);
Vertx.clusteredVertx(vertxOptions, ar->{
});
然后就可以啟動了,首先呢,我們需要注意兩點(diǎn):
第一點(diǎn):這里沒有配置EventBusOptions,我們不要在evenbusoption里面配置 clusterhost和port這類的東西,vertx instance 啟動成功之后,VertxUtil.getVertxInstance().eventBus()就可以拿到集群的模式下event bus instance,如果配置了,可能eventbus 之間的通信會一直存在問題,連接refused
第二點(diǎn):我們需要在vertxOption里面顯示的去設(shè)置setClusterHost 和?setClusterPort,這個(gè)Ip地址和port就是event bus 通信的 ip和端口號,顯示的指明之后,eventbus 通信就不會報(bào)錯(cuò)。這里再貼一下 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通信了,這個(gè)時(shí)候就不會有問題了。
但是當(dāng)我們想讓vertx? ecs cluster 集群 和 docker 節(jié)點(diǎn)之間通信,那么我們應(yīng)該如何配置呢?我是按照下面的方式配置,結(jié)果是正確的(折騰好長時(shí)間,一直是網(wǎng)絡(luò)通信的問題)
第一: docker-compose中填寫下面的信息
vertx-instal3:
? build: ./docker
? hostname: ech-10-168-128-77
? privileged: true
? ports:
? ? - "12001:12001"
? ? - "12002:12002"
? ? - "41000-47000:41000-47000" (這個(gè)端口號映射最好映射全點(diǎn),vertx里面會有很多端口號,我沒有仔細(xì)搞搞這個(gè)端口號,缺少和主機(jī)端口號映射的話,會導(dǎo)致網(wǎng)絡(luò)通信問題)
第二步:配置Dockerfile 文件,dockerfile文件的內(nèi)容如下:
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"]
最后執(zhí)行docker-compose build / up 的命令就可以了。 請記住,根據(jù)gossip 協(xié)議,p2p 的網(wǎng)絡(luò),docker 這個(gè)節(jié)點(diǎn),連接cluster 時(shí)候,配置的IP 也是 上面提到的IP,不需要改變。只要有一個(gè)可以連接通,docker這個(gè)節(jié)點(diǎn)就會加入到這個(gè)集群。
好了,我把ecs 和docker 集群網(wǎng)絡(luò)配置好了, vertx 就顯得簡單很多,按照異步框架寫代碼就可以了。
關(guān)于vertx evenbus local 和cluster 模式的分析,請看很下面的博客,分析的很詳細(xì):
https://blog.csdn.net/sdmjhca/article/details/78612792
https://www.sczyh30.com/posts/Vert-x/vertx-advanced-clustered-event-bus-internal/