Hazelcast简介
Hazelcast 是一个分布式计算和缓存平台。Hazelcast 采用 Java 语言实现,拥有 Java、C++、.NET、REST、Python、Go 和 Node.js 客户端。Hazelcast 还支持 Memcached 和 REST 协议。它最常使用的场景:在服务器A上缓存的数据,可以在服务器B上直接使用,感觉就像用本地缓存一样。
其主要功能有:
- 提供了 Map、Queue、MultiMap、Set、List、Semaphore、Atomic 等接口的分布式实现
- 提供了基于Topic 实现的消息队列或订阅\发布模式;
- 提供了分布式id生成器(IdGenerator)
- 提供了分布式事件驱动(Distributed Events)
- 提供了分布式计算(Distributed Computing)
- 提供了分布式查询(Distributed Query)
官网文档地址:
docs.hazelcast.com/hazelcast/l…
课外知识:
Hazelcast缓存框架背后的公司也是叫Hazelcast。
Hazelcast公司的名称来源于两个单词:Hazel和cast。Hazel是一种植物(榛树),而cast则表示将数据从一个地方传输到另一个地方。因此,Hazelcast的名称意味着将数据从一个地方传输到另一个地方,就像植物的花粉一样。
重要说明:
Hazelcast 不再支持 JDK 8 作为 Hazelcast 5.3.0 及更高版本的运行时。需要JDK 11+。
JDK8所能支持的最高版本为Hazelcast5.2.X版本。所以本文以最新版本Hzelcast5.2.4版本为例进行讲述
虽然,我在JDK8中使用最新版本Hazelcast5.3.1没有遇到问题,但是避免不必要的麻烦,如果用JDK8,那么最高就不要超过5.2版本
Hazelcast版本区别
Hazelcast 分为开源版和商用版,开源版本遵循 Apache License 2.0 开源协议可以免费使用,商用版本需要获取特定的License。 两者之间最大的区别在于:商用版本提供了数据的高密度存储。
我们知道在JVM中,有自己特定的GC机制,无论数据是在堆中还是栈中,只要发现无效引用的数据块,就有可能被回收。而Hazelcast的分布式数据都存放在JVM的内存中,频繁的读写数据会导致大量的GC开销。使用商业版的Hazelcast会拥有高密度存储的特性,大大降低JVM的内存开销,从而降低GC开销。
商用版本初提供高密度存储外,还提供了更多的数据结构、更好的性能、更好的可扩展性、更好的安全性等。此外,商用版本还提供了更好的支持和服务,例如,技术支持、培训、咨询等。
针对springboot项目,开源版和商用版不同的引入方式:
-
开源版
xml
代码解读
复制代码
<dependency> <groupId>com.hazelcast</groupId> <artifactId>hazelcast</artifactId> <version>5.2.4</version> </dependency> -
商用版
xml
代码解读
复制代码
<dependency> <groupId>com.hazelcast</groupId> <artifactId>hazelcast-enterprise</artifactId> <version>5.2.4</version> </dependency>
hazelcast集群方式
1.嵌入式方式

这种嵌入式方式集群在springboot项目中非常的简单,由于hazelcast本身就是java编写的,我们在pom文件中引入hazelcast,几乎不需要什么代码,就能把带有集群特性的hazelcast跑起来。当一台机器上改写缓存记录时,hazelcast负责将改动分发到集群中的其他机器中。而加入集群的发现机制,如果我们在不配置的情况下,会采用多播方式查找同一网络中的其他成员。
该方式的优点:
-
设置集群容易
上面用描述过这种方式非常的简单,只需要几行代码或者几句配置就能创建集群。
-
数据访问非常快
访问数据的时候,由于访问的是本地缓存,没有网络开销,所以访问非常快
该方式的缺点:
-
复制和同步代价很大
缓存中添加或更新记录时,该记录都会与集群的其他成员同步,这会导致大量的网络通信。
-
消耗大量的内存
当集群中的节点过多时,由于集群中的每个成员机,都会有其他成员的部分或者全部备份。那么会消耗大量的内存。
-
仅能用于java
springboot项目中引入一个jar包,就能实现集群,这种方式仅仅用于java,其他语言无法引入jar包。
hazelcast关键配置:
ini
代码解读
复制代码
Hazelcast.newHazelcastInstance();
2.客户端-服务器方式

这里member1到member4是Hazelcast集群的成员,他们构成了缓存集群。Hazelcast是通过外部访问的形式,和该集群进行通讯。相当于现在springboot程序,是一个客户端来访问Hazelcast集群。Hazelcast 使用 TCP socket通信。这时,就不仅仅可以使用java来访问这个缓存集群了。
该方式的优点:
-
缓存具有更好的扩展性
这里的缓存集群是独立的,避免了上面说的缓存浪费的情况,而且便于扩展集群数量。
-
更容易找到问题
由于缓存和程序是独立的,如果发生异常时,便于程序定位问题。
-
可以针对不同语言的客户端
客户端可以不仅仅是java语言,还能支持REST、Python、Go 和 Node.js 客户端。
该方式的缺点:
-
通讯时间更长
由于客户端和缓存集群有网络通讯的开销,所以可能会比嵌入式花费更长的时间。
-
版本兼容性问题
必须注意缓存集群和客户端之间的Hazelcast版本兼容性问题。
hazelcast关键配置:
ini
代码解读
复制代码
HazelcastClient.newHazelcastClient();
3.二级缓存方式

其实就是把上面两种方式进行结合。在取缓存值的时候,会首先从本地缓存读取,如果本地缓存没有找到数据,再从远端的缓存集群中请求数据并且将其添加到本地缓存中。当应用程序想要再次读取该数据时,可以在本地缓存中找到它。这样可以减少网络流量。但是凡事有利有弊,用二级缓存是我们必须接受可能的数据不一致。这是由于本地缓存有自己的缓存配置,它会根据这个配置失效数据。如果缓存集群中的数据被更新或删除,而我们在本地缓存中仍然可能有过时的数据。要跟进实际业务具体情况具体分析。不过一般情况是用不到二级缓存的。
hazelcast关键配置:
ini
代码解读
复制代码
HazelcastClient.newHazelcastClient(createClientConfig()); private ClientConfig createClientConfig() { ClientConfig clientConfig = new ClientConfig(); clientConfig.addNearCacheConfig(createNearCacheConfig()); return clientConfig; } private NearCacheConfig createNearCacheConfig() { NearCacheConfig nearCacheConfig = new NearCacheConfig(); nearCacheConfig.setName("mymap"); nearCacheConfig.setTimeToLiveSeconds(360); nearCacheConfig.setMaxIdleSeconds(60); return nearCacheConfig; }
Hazelcast存储数据的实现过程
1.Hazelcast分区
由于Hazelcast 服务之间是端对端的,没有主从之分,集群中所有的节点都存储等量的数据以及进行等量的计算。
Hazelcast 默认情况下把数据存储在 271 个区上,这个值可以通过系统属性 hazelcast.partition.count来配置。
2.Hazelcast分区存储原理
对于一个给定的键,在经过序列化、哈希并对分区总数取模之后能得到此键对应的分区号,所有的分区等量的分布与集群中所有的节点中,每个分区对应的备份也同样分布在集群中。
也就是说 Hazelcast 会使用哈希算法对数据进行分区,比如对于一个给定的map中的键,或者topic和list中的对象名称,分区存储的过程如下:
- 先序列化此键或对象名称,得到一个byte数组;
- 然后对上面得到的byte数组进行哈希运算;
- 再进行取模后的值即为分区号;
- 最后每个节点维护一个分区表,存储着分区号与节点之间的对应关系,这样每个节点都知道如何获取数据。
3.Hazelcast集群实现原理
Hazelcast通过分片来存储和管理所有进入集群的数据,采用分片的方案目标是保证数据可以快速被读写、通过冗余保证数据不会因节点退出而丢失、节点可线性扩展存储能力。下面将从理论上说明Hazelcast是如何进行分片管理的。Hazelcast的每个数据分片(shards)被称为一个分区(Partitions)。分区是一些内存段,根据系统内存容量的不同,每个这样的内存段都包含了几百到几千项数据条目,默认情况下,Hazelcast会把数据划分为271个分区,并且每个分区都有一个备份副本。当启动一个集群成员时,这271个分区将会一起被启动。
-
下图展示了集群只有一个节点时的分区情况。

从一个节点的分区情况可以看出,当只启动一个节点时,所有的271个分区都存放在一个节点中
-
启动第二个节点,会出现下面这样的集群分区方式

其中黑色的字体表示分区,蓝色的字体表示备份。节点1存储了标号为1到135的分区,这些分区会同时备份到节点2中。而节点2则存储了136到271的分区,并备份到了节点1中。
-
再添加2个新的节点到集群中

Hazelcast会一个一个的移动分区和备份到新的节点中,使得集群数据分布平衡。实际中分区并不是有序的分布,而是随机分布,上面的示例只是为了方便理解,重要的是理解 Hazelcast 的平均分布分区以及备份。
这个备份数量是可以设置的:不管是xml,yaml或者java代码,配置都是差不多的,参考java代码如下
arduino
代码解读
复制代码
config.getMapConfig("my-map").setBackupCount(2);Hazelcast 默认备份的数量是1个,如果备份数量超过1时,每个节点会存放自己的数据以及其它节点上的备份。
嵌入式方式
1.目标
假设有登录用户的信息需要缓存,为了演示过期方便,设置30秒的有效期。看30秒后Hazelcast是不是自动删除了。这里采用map缓存所有用户信息。map的key是userId,而value就是User对象。
2.编码方式
2.1加入依赖
xml
代码解读
复制代码
<!-- Hazelcast --> <dependency> <groupId>com.hazelcast</groupId> <artifactId>hazelcast</artifactId> <version>5.2.4</version> </dependency> <dependency> <groupId>com.hazelcast</groupId> <artifactId>hazelcast-spring</artifactId> <version>5.2.4</version> <exclusions> <exclusion> <artifactId>hazelcast</artifactId> <groupId>com.hazelcast</groupId> </exclusion> </exclusions> </dependency>
说明:
- 其实在引入hazelcast-spring时,它本身就依赖了hazelcast。这里为什么都引入一遍呢?理由是springboot本身维护了hazelcast的版本。就拿现在做例子用的springboot2.7.10版本来说,它依赖的是hazelcast的5.1.15版本。而我们想使用比较新的hazelcast,所以这里从新指定了两个依赖的版本。
- hazelcast 和 hazelcast-spring 的区别在于,hazelcast-spring 是为了结合 Spring 使用,例如在 xml 配置中使用 <hz:hazelcast id="instance"> 这样的命名空间。而 hazelcast 的主要依赖只有一个,即 hazelcast-5.3.1.jar,引入这一个 jar 理论上就能使用 Hazelcast 了。
- 如果想使用springboot的cache和Hazelcast结合的话,那么引入hazelcast-spring还是不错的。
2.2配置类
scss
代码解读
复制代码
@EnableCaching @Configuration public class HazelcastConfig { @Bean public Config config() { Config config = new Config(); config.setInstanceName("hazelcast-instance"); config.setClusterName("dev"); // 设置驱逐策略 EvictionConfig evictionConfig = new EvictionConfig(); evictionConfig.setEvictionPolicy(EvictionPolicy.LFU); evictionConfig.setMaxSizePolicy(MaxSizePolicy.PER_NODE); evictionConfig.setSize(542); // 设置map配置 MapConfig mapUserConfig = new MapConfig(); mapUserConfig.setName(Constants.CACHE_NAME_SESSION_USERS) .setBackupCount(2) .setTimeToLi


686

被折叠的 条评论
为什么被折叠?



