本文是关于Hazelcast的入门,Hazelcast是分布式数据存储中间件(当然不仅仅是数据存储功能)。
关于Hazelcast的介绍,简书上其它小伙伴写的文章讲解的非常好:【Hazelcast系列一】Hazelcast 概览
1. 简单介绍
1.1 Hazelcast公司提供了两个版本的产品
关于两个版本的对比,戳:https://hazelcast.com/pricing/
- Hazelcast open source edition(开源版本)
- Hazelcast enterprise edition(收费版本,提供了更高级的功能)
1.2 Hazelcast最些年也更新了很多版本:
- 2019.04.09发布了Hazelcast IMDG 3.12版本:https://hazelcast.com/blog/hazelcast-imdg-3-12-is-released/
- 2020.02.04发布了Hazelcast IMDG 4.0版本:https://hazelcast.com/blog/hazelcast-imdg-4-0-is-released/
- 2022.03.01发布了Hazelcast IMDG 5.1版本:https://hazelcast.com/blog/introducing-hazelcast-platform-5-1/
值得注意的是,3.x版本到5.x版本,包名也有改动。比如IMap在3.x里从属于包com.hazelcast.core.IMap下,而5.x在com.hazelcast.map.IMap下,关于如何升级,官网也有详细说明:5.1: Upgrading from IMDG 3.12.x
1.3 Hazelcast的拓扑结构
Hazelcast集群有两种部署模式:内嵌模式,客户端/服务器模式。
1.4 附上网站:
- Hazelcast官网:https://hazelcast.com/
- Hazelcast开源版本的github地址:https://github.com/hazelcast/hazelcast
- 本文基于v5.1.1版本,在线文档:https://docs.hazelcast.com/hazelcast/5.1/
- 关于Hazelcast与Spring结合使用,配置参考:https://docs.hazelcast.com/hazelcast/5.1/configuration/configuring-within-spring
- Github:Hazelcast+Spring的示例:https://github.com/hazelcast/hazelcast/tree/5.1.z/hazelcast-spring
2. Hello World示例
- 基于Java8
- Hazelcast v5.1.1,使用的是内嵌模式
- Spring Boot v2.5.7
2.1 依赖
Spring Boot及test的依赖:略
Hazelcast的主要依赖只有一个,即hazelcast-5.1.1.jar,引入这一个jar理论上就能使用Hazelcast了,而加入hazelcast-spring.jar主要是为了结合Spring使用,例如在xml配置中使用<hz:hazelcast id="instance">这样的命名空间。
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
<version>5.1.1</version>
<exclusions>
<exclusion>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
</exclusion>
</exclusions>
</dependency>
2.2 配置
首先是application.yaml文件,主要是为了启动多个instance,而将server.port动态化。
server:
port: ${PORT:5010}
其次是hazelcast.xml,我选择使用<hz:hazelcast id="instance">这样的配置,主要是从3.x升级过来的时候,对我来说使用起来更加方便。
关于配置的sample,在上述#1.4中有附上网站。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:hz="http://www.hazelcast.com/schema/spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.hazelcast.com/schema/spring
http://www.hazelcast.com/schema/spring/hazelcast-spring.xsd">
<hz:hazelcast id="instance">
<hz:config>
<hz:cluster-name>dev</hz:cluster-name>
<hz:network port="5701" port-auto-increment="true">
<hz:join>
<hz:multicast enabled="false" multicast-group="224.2.2.3" multicast-port="54327"/>
<hz:tcp-ip enabled="true">
<hz:members>localhost</hz:members>
</hz:tcp-ip>
</hz:join>
</hz:network>
</hz:config>
</hz:hazelcast>
</beans>
使用@ImportResource导入hazelcast.xml:
@Configuration
@ImportResource(locations = "classpath:hazelcast.xml")
public class HazelcastConfig {
}
2.3 开始使用HazelcastInstance
在#2.2的配置后,就可以使用@Autowired来注入HazelcastInstance了,这个类是Hazelcast的核心类,通过它可以获得IMap, Iset, ITopic, IQueue等分布式结构的data,关于这块戳:https://docs.hazelcast.com/hazelcast/5.1/data-structures/distributed-data-structures
以下例子暴露了两个API:
- /test/put?key={String}&value={String},往IMap中put数据。
- /test/get?key={String},从IMap中get数据。
@RestController
public class TestController {
@Autowired
private HazelcastInstance hazelcastInstance;
@GetMapping("test/put")
public boolean put(String key, String value) {
IMap iMap = hazelcastInstance.getMap("testMap");
iMap.put(key, value);
return true;
}
@GetMapping("test/get")
public String get(String key) {
IMap iMap = hazelcastInstance.getMap("testMap");
return iMap.containsKey(key) ? iMap.get(key).toString() : null;
}
}
2.4 测试
为了测试分布式,开启了两个服务,一个是5010端口,一个是5011端口,通过VM options动态传入端口号。

先启动端口为5010的程序,日志如下:
2022-05-14 22:52:41.369 INFO 95845 --- [ main] c.h.internal.cluster.ClusterService : [localhost]:5701 [dev] [5.1.1]
Members {size:1, ver:1} [
Member [localhost]:5701 - 699774f5-1982-400a-9033-6625d46e9d06 this
]
2022-05-14 22:52:41.386 INFO 95845 --- [ main] com.hazelcast.core.LifecycleService : [localhost]:5701 [dev] [5.1.1] [localhost]:5701 is STARTED
再启动端口为5011的程序,日志如下:
可以看到在5011启动之后Members变为2台:
2022-05-14 22:54:43.094 INFO 95894 --- [ration.thread-0] c.h.internal.cluster.ClusterService : [localhost]:5702 [dev] [5.1.1]
Members {size:2, ver:2} [
Member [localhost]:5701 - 699774f5-1982-400a-9033-6625d46e9d06
Member [localhost]:5702 - 7c902d8c-adbd-4b5e-8012-7c46ead9de36 this
]
2022-05-14 22:54:43.103 INFO 95894 --- [ main] com.hazelcast.core.LifecycleService : [localhost]:5702 [dev] [5.1.1] [localhost]:5702 is STARTED
开始测试:
- 通过5010端口先put数据:http://localhost:5010/test/put?key=testKey&value=testValue
- 再通过5011端口拿到value:http://localhost:5011/test/get?key=testKey,可以拿到5010 put的value:testValue
3. 配置解释
3.1 cluster-name
关于#2.2配置里的一些参数说明。
5.1版本里的cluster-name,在3.x里就是group的配置,就是组成集成的名字,如果cluster-name不一样,就不能组成一个集群,比如:
instance-01配置:<hz:cluster-name>dev</hz:cluster-name>
日志:
Members {size:1, ver:1} [
Member [localhost]:5701 - 12d30f00-0fde-4416-8606-4b695a1fcc91 this
]
2022-05-14 23:03:51.941 INFO 96095 --- [ main] com.hazelcast.core.LifecycleService : [localhost]:5701 [dev] [5.1.1] [localhost]:5701 is STARTED
instance-02配置:<hz:cluster-name>dev-02</hz:cluster-name>
日志如下:可以看到dev-02和dev并不会组成同一个cluster集群,在日志中也会打印warn级别的日志,提示5701端口已经被占用了,从而使用5702节点。
Members {size:1, ver:1} [
Member [localhost]:5702 - 9d343cea-a482-4e21-b26f-695e54dcb164 this
]
2022-05-14 23:04:03.917 WARN 96101 --- [ main] com.hazelcast.instance.impl.Node : [localhost]:5702 [dev-02] [5.1.1] Config seed port is 5701 and cluster size is 1. Some of the ports seem occupied!
2022-05-14 23:04:03.926 INFO 96101 --- [ main] com.hazelcast.core.LifecycleService : [localhost]:5702 [dev-02] [5.1.1] [localhost]:5702 is STARTED
3.2 集群中节点发现机制
官网:https://docs.hazelcast.com/hazelcast/5.1/clusters/discovery-mechanisms
Hazelcast提供了很多种节点发现的方式:
- Multicast(默认为true,
<xs:complexType name="multicast"><xs:attribute name="enabled" type="xs:string" default="true"/></xs:complexType>) - TCP
- 其它:Eureka Cloud Discovery \ Zookeeper Cloud Discovery \ Hazelcast for Kubernetes \ Hazelcast for Tanzu VMware
用的比较多的应该是前两种,即Multicast多播和TCP(Unicast,即单播),关于这两者最大的区别应该是:TCP是单播的,因为它是面向链接的可靠传输协议,因为它总是需要确认报文。而Multicast多播是基于UDP的,它可以实现一对多。
关于Multicast和TCP/IP,可以参考以下文章:
~ Is multicast possible in tcp?
~ Multicast
~ 什么是 Multicast|组播
如果使用TCP作为发现机制,可以定义members,可以按ip区段定义,也可以逗号隔开等等,参考:https://docs.hazelcast.com/hazelcast/5.1/clusters/discovering-by-tcp
