在一个项目中,我正在私有云中设置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);
}
@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