在一个项目中,我正在私有云中设置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 Discovery和ZooKeeper的概念证明。 首先,让我们跳过普通的旧Java代码中的hazelcast.xml
配置和引导集群:
@Configuration
public class HazelcastConfiguration {@Bean(destroyMethod = "shutdown")HazelcastInstance hazelcast(Config config) {return Hazelcast.newHazelcastInstance(config);}@BeanConfig config(ApplicationContext applicationContext, NetworkConfig networkConfig) {final Config config = new Config();config.setNetworkConfig(networkConfig);config.getGroupConfig().setName(applicationContext.getId());return config;}@BeanNetworkConfig networkConfig(@Value("${hazelcast.port:5701}") int port, JoinConfig joinConfig) {final NetworkConfig networkConfig = new NetworkConfig();networkConfig.setJoin(joinConfig);networkConfig.setPort(port);return networkConfig;}@BeanJoinConfig 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;}@BeanTcpIpConfig 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 {@BeanWithLifecycleServiceDiscovery<Void> serviceDiscovery(CuratorFramework curatorFramework, ServiceInstance<Void> serviceInstance) throws Exception {return ServiceDiscoveryBuilder.builder(Void.class).basePath("hazelcast").client(curatorFramework).thisInstance(serviceInstance).build();}@BeanWithLifecycleCuratorFramework curatorFramework(@Value("${zooKeeper.url:localhost:2181}") String zooKeeperUrl) {ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3);return CuratorFrameworkFactory.newClient(zooKeeperUrl, retryPolicy);}@BeanServiceInstance<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