使用Curator和ZooKeeper发现Hazelcast成员

在一个项目中,我正在私有云中设置Hazelcast集群。 在群集内,所有节点都必须互相看见,因此在引导过程中,Hazelcast将尝试查找其他群集成员。 没有服务器,并且所有节点都相等。 在Hazelcast中实现了多种发现成员的技术。 不幸的是,它不是AWS,因此我们无法使用EC2自动发现,并且组播被阻止,因此内置的组播支持无用。 最后一种选择是TCP / IP群集 ,其中所有节点的地址都需要在XML配置中进行硬编码:

<tcp-ip enabled="true">
    <member>machine1</member>
    <member>machine2</member>
    <member>machine3:5799</member>
    <member>192.168.1.0-7</member>
    <member>192.168.1.21</member>
</tcp-ip>

这不能很好地扩展,我们的云中的节点也是动态分配的,因此不可能在运行前确定地址。 在这里,我在下面展示基于Curator Service DiscoveryZooKeeper的概念证明。 首先,让我们跳过普通的旧Java代码中的hazelcast.xml配置和引导集群:

@Configuration
public class HazelcastConfiguration {
 
    @Bean(destroyMethod = "shutdown")
    HazelcastInstance hazelcast(Config config) {
        return Hazelcast.newHazelcastInstance(config);
    }
 
    @Bean
    Config config(ApplicationContext applicationContext, NetworkConfig networkConfig) {
        final Config config = new Config();
        config.setNetworkConfig(networkConfig);
        config.getGroupConfig().setName(applicationContext.getId());
        return config;
    }
 
    @Bean
    NetworkConfig networkConfig(@Value("${hazelcast.port:5701}") int port, JoinConfig joinConfig) {
        final NetworkConfig networkConfig = new NetworkConfig();
        networkConfig.setJoin(joinConfig);
        networkConfig.setPort(port);
        return networkConfig;
    }
 
    @Bean
    JoinConfig joinConfig(TcpIpConfig tcpIpConfig) {
        final JoinConfig joinConfig = disabledMulticast();
        joinConfig.setTcpIpConfig(tcpIpConfig);
        return joinConfig;
    }
 
    private JoinConfig disabledMulticast() {
        JoinConfig join = new JoinConfig();
        final MulticastConfig multicastConfig = new MulticastConfig();
        multicastConfig.setEnabled(false);
        join.setMulticastConfig(multicastConfig);
        return join;
    }
 
    @Bean
    TcpIpConfig tcpIpConfig(ApplicationContext applicationContext, ServiceDiscovery<Void> serviceDiscovery) throws Exception {
        final TcpIpConfig tcpIpConfig = new TcpIpConfig();
        final List<String> instances = queryOtherInstancesInZk(applicationContext.getId(), serviceDiscovery);
        tcpIpConfig.setMembers(instances);
        tcpIpConfig.setEnabled(true);
        return tcpIpConfig;
    }
 
    private List<String> queryOtherInstancesInZk(String name, ServiceDiscovery<Void> serviceDiscovery) throws Exception {
        return serviceDiscovery
                .queryForInstances(name)
                .stream()
                .map(ServiceInstance::buildUriSpec)
                .collect(toList());
    }
 
}

我使用applicationContext.getId()避免对应用程序名称进行硬编码。 在Spring Boot中,可以用--spring.application.name=...替换它config.getGroupConfig().setName(...)为集群config.getGroupConfig().setName(...)分配名称也是一个好主意–这将允许我们运行多个集群在同一网络中,即使启用了多播也是如此。 最后一个方法queryOtherInstancesInZk()最有趣。 创建TcpIpConfig我们手动提供其他群集成员所在的TCP / IP地址列表。 我们不是硬编码此列表(如上面的XML示例),而是从Curator查询ServiceDiscovery 。 我们请求应用程序的所有实例,并将其传递给TcpIpConfig 。 在进入Curator配置之前,只需几句话就可以解释Hazelcast如何使用TCP / IP配置。 显然,并非所有节点都同时启动。 当第一个节点启动时,Curator几乎不会返回一个实例(我们自己),因此群集将只有一个成员。 当第二个节点启动时,它将看到已经启动的节点并尝试与之形成集群。 显然,第一个节点将发现刚刚连接到它的第二个节点。 归纳继续进行-当更多的节点启动时,它们从Curator服务发现中获取现有节点并加入它们。 Hazelcast会通过从群集中删除成员并重新平衡数据来避免成员的虚假崩溃。 另一方面,馆长会将其从ZooKeeper中删除。

好吧,现在ServiceDiscovery<Void>来自哪里? 这是完整的配置:

@Configuration
public class CuratorConfiguration {
 
    @BeanWithLifecycle
    ServiceDiscovery<Void> serviceDiscovery(CuratorFramework curatorFramework, ServiceInstance<Void> serviceInstance) throws Exception {
        return ServiceDiscoveryBuilder
                .builder(Void.class)
                .basePath("hazelcast")
                .client(curatorFramework)
                .thisInstance(serviceInstance)
                .build();
    }
 
    @BeanWithLifecycle
    CuratorFramework curatorFramework(@Value("${zooKeeper.url:localhost:2181}") String zooKeeperUrl) {
        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3);
        return CuratorFrameworkFactory.newClient(zooKeeperUrl, retryPolicy);
    }
 
    @Bean
    ServiceInstance<Void> serviceInstance(@Value("${hazelcast.port:5701}") int port, ApplicationContext applicationContext) throws Exception {
        final String hostName = InetAddress.getLocalHost().getHostName();
        return ServiceInstance
                .<Void>builder()
                .name(applicationContext.getId())
                .uriSpec(new UriSpec("{address}:{port}"))
                .address(hostName)
                .port(port)
                .build();
    }
 
}

默认情况下,Hazelcast监听5701,但是如果指定的端口被占用,它将尝试后续端口。 启动时,我们在Curator中注册自己,提供主机名和Hazelcast端口。 当我们的应用程序的其他实例启动时,他们将看到先前注册的实例。 当应用程序出现故障时,Curator将使用ZooKeeper中的临时节点机制注销我们的注册。 顺便说一句@BeanWithLifecycle不是来自Spring或Spring Boot,我自己创建了它以避免重复:

@Target({METHOD, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Bean(initMethod = "start", destroyMethod = "close")
@interface BeanWithLifecycle { }

运行ZooKeeper(默认情况下在localhost:2181 ),我们可以启动任意数量的节点,它们将很快相互找到对方。 唯一的共享信息是ZooKeeper URL。

翻译自: https://www.javacodegeeks.com/2014/12/hazelcast-member-discovery-using-curator-and-zookeeper.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值