likes
comments
collection
share

SpringBoot 整合 hazelcast 分布式缓存技术

作者站长头像
站长
· 阅读数 51

🙏废话不多说系列,直接开整🙏

SpringBoot 整合 hazelcast 分布式缓存技术

一、Hazelcast 认识

(1)简介

    Hazelcast 是由 Hazelcast 公司开发的一款开源的分布式内存级别的缓存数据库,可以为基于JVM环境运行的各种应用提供分布式集群和分布式缓存服务。

    利用 Hazelcast 可以满足“分布式”、“集群服务”、“网格式内存数据”、“分布式缓存“、“弹性可伸缩服务”等的要求。

(2)应用
  • Hazelcast 提供了对很多 Java 接口的分布式实现,如Map, Queue, ExecutorService, Lock以及 JCache。它以一个 JAR 包的形式提供服务,并且提供了 Java, C/C++, .NET 以及 REST 客户端
  • 在应用时,可以将Hazelcast的jar包直接嵌入到任何使用Java、C++、.NET 开发的产品中,我们只需要在应用中引入一个jar包,进行简单的配置和编码就可以实现。

目前 Hazelcast 已经更新到 4.X 版本。

(3)版本区别
  • Hazelcast 分为开源版商用版,开源版本遵循 Apache License 2.0 开源协议可以免费使用,商用版本需要获取特定的License。两者之间最大的区别在于:商用版本提供了数据的高密度存储。

  • 我们知道在JVM中,有自己特定的GC机制,无论数据是在堆中还是栈中,只要发现无效引用的数据块,就有可能被回收。而Hazelcast的分布式数据都存放在JVM的内存中,频繁的读写数据会导致大量的GC开销。使用商业版的Hazelcast会拥有高密度存储的特性,大大降低JVM的内存开销,从而降低GC开销

(4)特性
  1. 自治集群(无中心化)

  2. 数据按应用分布式存储;

  3. 抗单点故障;

  4. 简单易用;

  5. 其他特性:① 服务器/客户端模型;② 支持脚本管理;③能够和Docker快速整合等;

(5)功能
  • 提供了分布式id生成器(IdGenerator);
  • 提供了分布式事件驱动(Distributed Events);
  • 提供了分布式计算(Distributed Computing);
  • 提供了分布式查询(Distributed Query)。
  • 提供java.util.{Queue, Set, List, Map}分布式实现。
  • 提供java.util.concurrency.locks.Lock分布式实现。
  • 提供java.util.concurrent.ExecutorService分布式实现。
  • 提供用于一对多关系的分布式MultiMap。
  • 提供用于发布/订阅的分布式Topic(主题)。
  • 通过JCA与J2EE容器集成和事务支持。
  • 提供用于安全集群的Socket层加密。
  • 支持同步和异步持久化。
  • 为Hibernate提供二级缓存Provider 。
  • 通过JMX监控和管理集群。
  • 支持动态HTTP Session集群。
  • 利用备份实现动态分割。
  • 支持动态故障恢复。

总的来说,在独立JVM中经常使用的数据结果或模型,Hazelcast 都提供了分布式集群的实现。

二、Hazelcast 原理

    Hazelcast 提供了 Map、Queue、MultiMap、Set、List、Semaphore、Atomic 等常用接口的分布式实现。

    以Map接口为例,当我们通过Hazelcast创建一个Map实例后,我们在节点A调用 Map::put("A","A_DATA") 方法添加数据,然后可以在节点B使用 Map::get("A") 获取到值为"A_DATA" 的数据。

三、Hazelcast 存储数据的实现过程

(1)Hazelcast 分区
  • 由于Hazelcast 服务之间是端对端的,没有主从之分,集群中所有的节点都存储等量的数据以及进行等量的计算。

  • Hazelcast 默认情况下把数据存储在 271 个区上,这个值可以通过系统属性 hazelcast.partition.count来配置。

(2)Hazelcast 分区存储原理

    对于一个给定的键,在经过序列化、哈希并对分区总数取模之后能得到此键对应的分区号,所有的分区等量的分布与集群中所有的节点中,每个分区对应的备份也同样分布在集群中。

    也就是说 Hazelcast 会使用哈希算法对数据进行分区,比如对于一个给定的map中的键,或者topic和list中的对象名称,分区存储的过程如下:

  1. 先序列化此键或对象名称,得到一个byte数组;

  2. 然后对上面得到的byte数组进行哈希运算;

  3. 再进行取模后的值即为分区号;

  4. 最后每个节点维护一个分区表,存储着分区号与节点之间的对应关系,这样每个节点都知道如何获取数据。

(3)Hazelcast 集群实现原理

    Hazelcast通过分片来存储和管理所有进入集群的数据,采用分片的方案目标是保证数据可以快速被读写、通过冗余保证数据不会因节点退出而丢失、节点可线性扩展存储能力。下面将从理论上说明Hazelcast是如何进行分片管理的。

【1】分片

Hazelcast的每个数据分片(shards)被称为一个分区(Partitions)。分区是一些内存段,根据系统内存容量的不同,每个这样的内存段都包含了几百到几千项数据条目,默认情况下,Hazelcast会把数据划分为271个分区,并且每个分区都有一个备份副本。当启动一个集群成员时,这271个分区将会一起被启动。

① 集群只有一个节点时的分区情况:

SpringBoot 整合 hazelcast 分布式缓存技术

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

集群中最老的节点(或者说最先启动)负责定时发送分区表到其他节点,这样如果有其他节点加入或者离开集群,所有的节点也能更新分区表。

这个定时任务时间间隔可以通过系统属性 hazelcast.partition.table.send.interval来配置,缺省值为15秒。

  • 重分区发生在:① 节点加入集群;② 节点离开集群;(此时最老节点会更新分区表,然后分发,再接着集群开始移动分区,或者从备份恢复分区。)
  • 如果 老节点挂了,此老节点会接手这个任务。

四、Hazelcast 的使用方式

有两种方式:① 嵌入式;② 客户端/服务器模式;

(1)嵌入式

Hazelcast 服务器的 jar 包被导入到宿主应用程序中,服务器启动后缓存数据会被存在于各个宿主应用中,优点是可以更低延迟的数据访问。

SpringBoot 整合 hazelcast 分布式缓存技术

(2)客户端 / 服务器

Hazelcast 客户端的 jar 包被导入宿主应用程序中,服务器 jar 包独立运行于 JVM 中。优点是更容易调试以及有更可靠的性能,最重要的是有更好的扩展性。

SpringBoot 整合 hazelcast 分布式缓存技术

五、SpringBoot 整合 Hazelcast

  • JDK 8+
  • SpringBoot 2.4+
(1)引入依赖
<!--hazelcast 核心依赖包-->
<dependency>
   <groupId>com.hazelcast</groupId>
   <artifactId>hazelcast</artifactId>
</dependency>
<dependency>
   <groupId>com.hazelcast</groupId>
   <artifactId>hazelcast-spring</artifactId>
</dependency>
(2)核心代码
① 配置application.properties
# 应用名称
spring.application.name=springboot-hazelcast
# 应用服务 WEB 访问端口
server.port=8080
② 添加 hazelcast 配置类
@Configuration
public class HazelcastConfiguration {
    @Bean
    public Config hazelCastConfig() {
        Config config = new Config();
        // 解决同网段下,不同库项目
        GroupConfig gc = new GroupConfig("hazelGroup");
        config.setInstanceName("hazelcast-instance")
            .addMapConfig(new MapConfig()
                          .setName("configuration")
                          // Map 中存储条目的最大值。[0 ~ Integer.MAX_VALUE] 默认值为0
                          .setMaxSizeConfig(new MaxSizeConfig(200, MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE))
                          // 数据释放策[NONE/LRU/LFU] 这是 Map作为缓存的一个参数,用于指定数据的回收算法。默认为 NONE,LRU:最近最少使用策略。
                          .setEvictionPolicy(EvictionPolicy.LRU)
                          // 数据留存时间(0~Integer.MAX_VALUE,缓存相关参数,单位秒,默认为0)
                          .setTimeToLiveSeconds(-1)).setGroupConfig(gc);
        return config;
    }
}
③ 控制层测试
package edu.study.module.springboothazelcast.controller;

import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.IList;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;

import java.util.Map;
import java.util.Queue;

/**
 * 分布式缓存技术控制层
 *
 * @author drew
 * @date 2021/2/5 15:06
 **/
@Slf4j
@RestController
@RequestMapping(path = "/hazelcast")
public class HazelcastController {
    @Autowired
    @Qualifier("hazelcastInstance")
    private HazelcastInstance hazelcastInstance;

    @PostMapping(value = "/save")
    public String saveMapData(@RequestParam String key, @RequestParam String value) {
        Map<String, String> hazelcastMap = hazelcastInstance.getMap("hazelcastMap");
        hazelcastMap.put(key, value);
        return "success";
    }

    @GetMapping(path = "/get")
    public String getMapData(@RequestParam String key) {
        Map<String, String> hazelcastMap = hazelcastInstance.getMap("hazelcastMap");
        return hazelcastMap.get(key);
    }

    @GetMapping(path = "/all")
    public Map<String, String> readAllDataFromHazelcast() {
        return hazelcastInstance.getMap("hazelcastMap");
    }

    @GetMapping(path = "/list")
    public String saveList(@RequestParam(required = false) String value) {
        // 创建集群 list
        IList<Object> clusterList = hazelcastInstance.getList("myList");
        clusterList.add(value);
        return "success";
    }

    @DeleteMapping(value = "/showList")
    public IList<Object> showList() {
        return hazelcastInstance.getList("myList");
    }

    @GetMapping(value = "/clearList")
    public String clearList() {
        IList<Object> clusterList = hazelcastInstance.getList("myList");
        clusterList.clear();
        return "success";
    }

    @GetMapping(value = "/queue")
    public String saveQueue(@RequestParam String value) {
        // 创建集群Queue
        Queue<String> clusterQueue = hazelcastInstance.getQueue("myQueue");
        clusterQueue.offer(value);
        return "success";
    }

    @GetMapping(value = "/showQueue")
    public Queue<String> showQueue() {
        Queue<String> clusterQueue = hazelcastInstance.getQueue("myQueue");
        for (String obj : clusterQueue) {
            log.warn("value=" + obj);
        }
        return clusterQueue;
    }

    @DeleteMapping(value = "/clearQueue")
    public String clearQueue() {
        Queue<String> clusterQueue = hazelcastInstance.getQueue("myQueue");
        clusterQueue.clear();
        return "success";
    }
}
(3)测试接口汇总
  1. map 类型的数据结构

    -- 存储元素到指定的 map(hazelcastMap)中
    请求方式:POST
    请求地址:http://localhost:8080/hazelcast/save
    数据:
      key:phone
      value:13798765425
    
    -- 查询指定元素
    请求方式:GET
    请求地址:http://localhost:8080/hazelcast/get?key=name
    
    -- 查询所有元素
    请求方式:GET
    请求地址:http://localhost:8080/hazelcast/all
    
  2. list 类型的数据结构

    -- 添加元素到指定集合
    请求方式:GET
    请求地址:http://localhost:8080/hazelcast/list?value=Fish
    
    -- 查询所有 list 元素
    请求方式:GET
    请求地址:http://localhost:8080/hazelcast/showList
    
    -- 清空指定集合
    请求方式:DELETE
    请求地址:http://localhost:8080/hazelcast/clearList
    
  3. queue 类型的数据结构

    1. 添加元素;
    2. 查询元素;
    3. 删除元素;
(4)hazelcast 延申内容
  1.  hazelcast管理终端:[Hazelcast IMDG](hazelcast.org/download/ar…)
  2. 注意 hazelcast IMDG 需要跟引入的hazelcast版本保持一致。
  3. hazelcast 特性:
    1. 无主从模式;(与许多Nosql 解决方案不同,hazelcast 节点是点对点的。没有主从关系;所以成员都存储相同数据的数据,并进行了相等的处理,避免了单点故障。)
    2. 弹性可扩展;(hazelcast 旨在扩展成千上万的成员。新成员启动,将自动发现集群,并线性增加存储和处理能力。成员之间通过 TCP 保持连接和通讯。)
    3. 读写快速高效:(hazelcast 所有数据都存储在内存中,提供基于内存快速高效的读写能力。)
  4. hazelcast 优势:
    1. 提供开源版本;
    2. 无需安装,只是个极小的 jar 包。
    3. 提供开箱即用的分布式数据结构,如 List, Map, Queue,MultiMap,Topic ,Lock 和 Executor。
    4. 集群非传统主从关系,避免了单点故障;集群中所有成员共同分担集群功能;
    5. 集群提供弹性扩展,新成员在内存不足或者负载过高时能动态加入集群。
    6. 集群中成员分担数据缓存的同时【相互冗余备份】其他成员数据,防止成员离线后数据丢失。
    7. 提供 SPI 接口支持支持用户自定义分布式数据结构。
  5. hazelcast 使用场景
    1. 频繁读写数据;
    2. 需要高可用分布式缓存;
    3. 内存行 nosql 存储;
    4. 分部署环境中弹性扩展;

附录


转载自:https://juejin.cn/post/7362842553845350437
评论
请登录