SpringBoot集成Hazelcast

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>

说明:

  1. 其实在引入hazelcast-spring时,它本身就依赖了hazelcast。这里为什么都引入一遍呢?理由是springboot本身维护了hazelcast的版本。就拿现在做例子用的springboot2.7.10版本来说,它依赖的是hazelcast的5.1.15版本。而我们想使用比较新的hazelcast,所以这里从新指定了两个依赖的版本。
  2. hazelcast 和 hazelcast-spring 的区别在于,hazelcast-spring 是为了结合 Spring 使用,例如在 xml 配置中使用 <hz:hazelcast id="instance"> 这样的命名空间。而 hazelcast 的主要依赖只有一个,即 hazelcast-5.3.1.jar,引入这一个 jar 理论上就能使用 Hazelcast 了。
  3. 如果想使用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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值