JAVA中使用最广泛的本地缓存?Ehcache的自信从何而来3 —— 本地缓存变身分布式集群缓存,打破本地缓存天花板

news2025/7/15 18:07:56

大家好,又见面了。


本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容,将会通过系列专题,讲清楚缓存的方方面面。如果感兴趣,欢迎关注以获取后续更新。


上一篇文章中,我们知晓了如何在项目中通过不同的方式来集成Ehcache并在业务逻辑中进行使用。作为JAVA本地缓存框架综合实力天花板级别的Ehcache,除了在本地缓存方面具有强悍的实力外,还具有一个其它对手所不具备的特色功能,即Ehcache提供了对于集群能力的支持,这也使得Ehcache不仅仅是个本地单机缓存,更是一个分布式缓存。

分布式缓存的意义是什么?集群方案又可以解决哪些问题?它与单机缓存有啥区别?与Redis等集中式缓存有啥不同?如何去选择、又该如何使用?带着这一连串的疑问,让我们一起探讨下Ehcache的各种不同集群方案,找出上述问题的答案。

本地缓存或者集中缓存的问题

在正式开始阐述Ehcache的集群解决方案前,先来做个铺垫,了解下单机缓存与集中式缓存各自存在的问题。

单机缓存不可言说的痛

对于单机缓存而言,缓存数据维护在进程中,应用系统部署完成之后,各个节点进程就会自己维护自己内存中的数据。在集群化部署的业务场景中,各个进程独自维护自己内存中的数据,而经由负载均衡器分发到各个节点进行处理的请求各不相同,这就导致了进程内缓存数据不一致,进而出现各种问题 —— 比较典型的就是缓存漂移问题。

缓存漂移,是单机缓存在分布式系统下无法忽视的一个问题。在这种情况下,大部分的项目使用中会选择避其锋芒、或者自行实现同步策略进行应对。常见的策略有:

  • 本地缓存中仅存储一些固定不变、或者不常变化的数据。

  • 通过过期重新加载、定时refresh等策略定时更新本地的缓存,忍受数据有一定时间内的不一致

  • 对于少量更新的场景,借助MQ构建更新机制,有变更就发到MQ中然后所有节点消费变更事件然后更新自身数据。

集中式缓存也并非万能银弹

在集群部署的场景下,为了简化缓存数据一致性方面的处理逻辑,大部分的场景会直接选择使用Redis等集中式缓存。集中式缓存的确是为分布式集群场景而生的,通过将缓存数据集中存放,使得每个业务节点读取与操作的都是同一份缓存记录。这样只需要由缓存服务保证并发原子性即可。

但集中式缓存也并非是分布式场景下缓存方案的万能银弹。

项目中使用缓存的目的,主要是为了提升整体的运算处理效率,降低对外的IO请求等等。而集中式缓存是独立于进程之外部署的远端服务,需要基于网络IO交互的方式来获取,如果一个业务逻辑中涉及到非常频繁的缓存操作,势必会导致引入大量的网络IO交互,进而导致非常严重的性能损耗

为了解决这个问题,很多时候还是需要本地缓存结合集中式缓存的方式,构建多级缓存的方式来解决。

Ehcache分布式集群方案

相比纯粹的本地缓存,Ehcache自带集群解决方案,通过相应的配置可以让本地缓存变身集群版本,以此来应付分布式场景下各个节点缓存数据不一致的问题,并且由于数据都缓存在进程内部,所以也可以避免集中是缓存频繁在业务流程中频繁网络交互的弊端。

Ehcache官方提供了多种集群方案供选择,下面一起看下。

RMI组播

RMI是一种点对点(P2P)的通信交互机制,Ehcache利用RMI来实现多个节点之间数据的互通有无,相互知会彼此更新数据。对于集群场景下,这就要求集群内所有节点之间要两两互通,组成一张网状结构。

在集群方式下进行数据通信交互,要求被传输的数据一定是要可序列化与反序列化的,对于JAVA而言,直白的说,就是对象一定是要实现了Serializable接口。

基于RMI组播的方式,Ehcache会向对应地址发送RMI UDP组播包,由于Ehcache对于组播的实现较为简单,所以在一些网络情况较为复杂的场景的支持度不是很完善,方案选择的时候需注意。此外,由于是即时消息模式,如果中途某个进程由于某些原因不可达,也可能会导致同步消息的丢失。所以对于可靠性以及数据一致性要求较高的场景需要慎选

JMS消息

JMS消息方案是一种很常用的Ehcache集群方案。JMS是一套JAVA中两个进程之间的异步通信API,定义了消息通讯所必须的一组通用能力接口,比如消息的创建、发送、接收读取等。

JMS也支持构建基于事件触发模型的消息交互机制,也即生产者消费者模式(又称发布订阅模式),其核心就是一个消息队列,集群内各个业务节点都订阅对应的消息队列topic主题,如果有数据变更事件,也发送到消息队列的对应的topic主题下供其它节点消费。

相比于RMI组播方式,JMS消息方式有个很大的优势在于不需要保证所有节点都全部同时在线,因为是基于发布订阅模式,所以即使有节点中途某些原因宕机又重启了,重启之后仍然可以接收其他节点已发布的变更,然后保证自己的缓存数据与其它节点一致。

Ehcache支持对接多种不同的MQ来实现基于JMS消息的集群组网方案,默认使用ActiveMQ,也可以切换为Kafka或者RabbitMQ等消息队列组件。

Cache Server模式

Ehcache的Cache Server是一种比较特殊的存在形式,它通常是一个独立的进程进行部署,然后多个独立的进程之间组成一个分布式集群。Cache Server是一个纯粹的缓存集群,对外提供restful接口或者soap接口,各个业务可以通过接口来获取缓存 —— 这个其实已经不是本地进程内缓存的概念了,其实就是一个独立的集中式缓存,类似Redis般的感觉。

看一下一个典型的高可用水平扩容模式的Cache Server组网与业务调用的场景示意图:

可以看到不管业务模块是用的什么编码语言,或者是什么形态的,都可以通过http接口去访问缓存数据,而Cache Server就是一个集中式缓存。在Cache Server中,集群内部可以有一个或者多个节点,这些节点具有完全相同的数据内容,做到了数据的冗余备份,而集群之间数据可以不同,实现了数据容量的水平扩展。

值得注意的一点是,如果你访问Ehcache的官网,会发现其官方提供的3.x版本的说明文档中不再有Cache Server的身影,而在2.x版本中都会作为一个单独的章节进行介绍。为什么在3.x版本中不再提供Cache Server模式呢?我在官方文档中没找到相关的说明,个人猜测主要有下面几个原因:

  • 定位过于尴尬,如果说要作为集中式缓存来使用,完全可以直接使用redis,没有必要费事劳神的去搭建Cache Server

  • Terracotta方式相比而言功能上更加的完备,兼具水平扩展与本地缓存的双重优势,完全可以取代Cache Server

JGroups方式

JGroups的方式其实和RMI有点类似。JGroups是一个开源的群组通讯工具,可以用来创建一个组,这个组中的成员可以给其他成员发送消息。其工作模式基于IP组播(IP multicast),但可以在可靠性和群组成员管理上进行扩展,而且JGroups的架构上设计非常灵活,提供可以兼容多种协议的协议栈。

JGroups的可靠性体现在下面几个方面:

  1. 对所有接收者的消息的无丢失传输(通过丢失消息的重发)
  2. 大消息的分割传输和重组
  3. 消息的顺序发送和接收
  4. 保证原子性,消息要么被所有接收者接收,要么所有接收者都收不到

也正是由于JGroups具备的上述诸多优秀特性,它常常被选择作为集群内各个节点之间数据同步的解决方案。而Ehcache也一样,支持基于JGroups实现的集群方案,通过IP组播消息,保证集群内各个节点之间数据的同步。

Terracotta方式

Terracotta是什么?看下来自百度百科的介绍:

Terracotta是一款由美国Terracotta公司开发的著名开源Java集群平台。它在JVM与Java应用之间实现了一个专门处理集群功能的抽象层,以其特有的增量检测、智能定向传送、分布式协作、服务器镜像、分片等技术,允许用户在不改变现有系统代码的情况下实现单机Java应用向集群化应用的无缝迁移。使得用户可以专注于商业逻辑的开发,由Terracotta负责实现高性能、高可用性、高稳定性的企业级Java集群。

所以说,Terracotta是一个JVM层专门负责做分布式节点间协同处理的平台框架。那么当优秀的JVM级缓存框架Ehcache与同样优秀的JVM间多节点协同框架Terracotta组合到一起,势必会有不俗的表现。

看下来自Ehcache官网的对于其Terracotta集群模式的图片说明:

基于Terracotta方式,Ehcache可以支持:

  • 热点数据存储在进程本地,然后根据热度进行优化存储,热度高的会优先存储在更快的位置(比如heap中)。

  • 存储在其中一台应用节点上的缓存数据,可以被集群中其它节点访问到。

  • 缓存数据在集群层面是完整的,也支持按照HA模式设定高可用备份。

可以说这种模式下,既保留了Ehcache本地缓存的超高处理性能,又享受到了分布式缓存带来的集群优势,不失为一种比较亮眼的组合。

引申思考 —— 本地缓存的设计边界与定位

如上所言,纵使Ehcache提供了多种集群化策略,但略显尴尬的是实际中各个公司项目并没有大面积的使用。其实分析下来也很好理解:

如果真的需要很明确的诉求去解决分布式场景下的缓存一致性问题,直接选择redis、memcache等主流的集中式缓存组件即可

所以Ehcache的整体综合功能虽然是最强大的,整体定位偏向于大而全,但也导致在各个细分场景下表现不够极致:

  • 相比Caffeine:略显臃肿, 因为提供了很多额外的功能,比如使用磁盘缓存、比如支持多节点间集群组网等;

  • 相比Redis: 先天不足,毕竟是个本地缓存,纵使支持了多种组网模式,依旧无法媲美集中式缓存在分布式场景下的体验。

但在一些相对简单的集群数据同步场景下,或者对可靠性要求不高的集群缓存数据同步场景下,Ehcache还是很有优势的、尤其是Terracotta集群模式,也不啻为一个很好的选择。

小结回顾

好啦,关于Ehcache的集群相关能力,就介绍到这里咯,而关于文章开头的几个问题,我们也在文章内容中做了解答与探讨。至此呢,我们关于Ehcache的相关介绍就全部结束了。那么你对Ehcache是否还有什么自己的观点呢?欢迎评论区一起交流下,期待和各位小伙伴们一起切磋、共同成长。

随着本篇Ehcache介绍文章的结束,我们缓存专栏关于主流本地缓存框架的介绍就告一段落了。下一篇文章开始,我们将开始将目光聚焦到集中式缓存的身上,比如大家耳熟能详的Redis,以及经常在面试中会拿来与Redis做比较的Memcache等等。如有兴趣,欢迎关注。

📣 补充说明1

本文属于《深入理解缓存原理与实战设计》系列专栏的内容之一。该专栏围绕缓存这个宏大命题进行展开阐述,全方位、系统性地深度剖析各种缓存实现策略与原理、以及缓存的各种用法、各种问题应对策略,并一起探讨下缓存设计的哲学。

如果有兴趣,也欢迎关注此专栏。

📣 补充说明2

  • 关于本文中涉及的演示代码的完整示例,我已经整理并提交到github中,如果您有需要,可以自取:https://github.com/veezean/JavaBasicSkills

我是悟道,聊技术、又不仅仅聊技术~

如果觉得有用,请点赞 + 关注让我感受到您的支持。全网同名,欢迎关注,获取更及时的更新。

期待与你一起探讨,一起成长为更好的自己。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/145745.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python】Numpy处理多项式类Polynomial

文章目录构造函数求导和积分求根和反演采样与拟合其他方法构造函数 Numpy中提供了多项式模块,里面封装了一些用以快速解决多项式问题的类和函数,其中最重要类的自然是Polynomial,其构造函数为 class numpy.polynomial.polynomial.Polynomia…

list容器的底层结构(详述insert()与erase())

目录 一、带头结点的双向循环链表(list) 二、贯穿list容器的insert与erase接口​编辑 一、带头结点的双向循环链表(list) 二、贯穿list容器的insert与erase接口 通过在指定位置的元素之前插入新元素来扩展容器。 这有效地增加了…

520页(17万字)集团大数据平台整体解决方案-v1.0

【版权声明】本资料来源网络,知识分享,仅供个人学习,请勿商用。【侵删致歉】如有侵权请联系小编,将在收到信息后第一时间删除!完整资料领取见文末,部分资料内容: 1.1.1 系统总体逻辑结构 4-14系…

Golang 面试题总结

一.基础部分 go语言的值类型和引用类型? 值类型:int、float、bool、string和数组这些类型都属于值类型。 值类型的变量直接指向存在内存中的值,值类型的变量的值存储在栈中。当使用 将一个变量的值赋给另一个变量时,如 j i ,实…

九、k8s 安全认证

文章目录1 访问控制概述2 认证管理3 授权管理4 准入控制1 访问控制概述 Kubernetes作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。所谓的安全性其实就是保证对Kubernetes的各种客户端进行认证和鉴权操作。 客户端 在Kubernetes集群中&#…

MySQL调优-深入理解MVCC机制

目录 MySQL调优-深入理解MVCC机制 MVCC多版本并发控制机制 undo日志版本链与read view机制详解 根据图2和图3对应画出下图的undo日志版本链: 版本链比对规则: 注意: 举例1:分析一下下图select1的read_view以及各个select语句…

Cadence PCB仿真使用Allegro PCB SI导入其他板卡的层叠结构的方法图文教程

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1,概述2,导入方法3,总结1,概述 本文详细介绍使用Allegro PCB SI PCB仿真软件导入其他电路板层叠结构的方法。 2,导入方法 第1步:打开待仿真的PCB文件,并确认软件为Allegro PCB SI 如果,打开软件不是Allegro PC…

STL-vector的接口使用及模拟实现

文章目录vector类的介绍vector类的常用接口介绍 构造相关 无参构造迭代器区间构造拷贝构造 容量相关的接口 sizereserveresizecapacityempty 数据访问及遍历相关的接口 operator[]begin endrbegin rend 修改数据相关的接口 push_backpop_backinserterase vector类的模拟实现…

excel统计函数:应用广泛的动态统计之王OFFSET 下篇

【前言】在上篇文章中,我们了解了OFFSET函数的运算原理和各个参数的作用,并且我们也通过一些OFFSET的案例,了解了它的用途。那么本篇我们继续来看看,OFFSET函数在实际工作中所能起到的强大效果吧。一、高阶应用的思路(…

vector使用指南

目录 引言 空间配置器 vector 与 string的一些差异 vector容器与string容器的一些差异 接口介绍——reserve resize接口 shrink_to_fit 接口 operator[ ] 和 at 接口 assign接口 增删查改接口 swap接口 例题讲解 引言 vector实质上就是数据结构的顺序表&#xff0…

数据结构:栈和队列(详细讲解)

🎇🎇🎇作者: 小鱼不会骑车 🎆🎆🎆专栏: 《数据结构》 🎓🎓🎓个人简介: 一名专科大一在读的小比特,努力学习编程是我唯一…

(8)Qt中的自定义信号

目录 自定义信号需要遵循的规则 信号的发送 自定义信号的基本实现 使用一个父子窗口切换的小案例 Qt框架提供的信号在某些特定场景下是无法满足我们的项目需求的,因此我们还设计自己需要的的信号,同样还是使用connect()对自定义的信号槽进行连接。 自…

制造业ERP管理系统解决方案之销售管理

在企业的生存发展中,销售管理起到了重要的作用,它决定着企业发展的提速和效益的提升。做好销售管理工作,不仅可以推动企业资金有效运转,还可以使企业在稳定中高效发展,使企业价值最大化。而在制造企业销售管理中&#…

Leetcode.1658 将 x 减到 0 的最小操作数

题目链接 Leetcode.1658 将 x 减到 0 的最小操作数 题目描述 给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。 如…

SHELL脚本学习 --- 第八次作业(安全脚本)

SHELL脚本学习 — 第八次作业 题目要求: 将密码输入错误超过4次的IP地址通过firewalld防火墙阻止访问 思路: 首先需要找到ssh密码输入错误超过四次的IP地址,需要到日志文件中找。 该日志文件为/var/log/secure。 找到之后通过正则匹配到密码输…

JavaEE高阶---SpringBoot 统⼀功能处理

一:什么是SpringBoot 统⼀功能处理 SpringBoot统一功能处理是AOP的实战环节。我们主要学习三方面内容: 统一用户登录权限验证;统一数据格式返回;统一异常处理。 二:统一用户登录权限验证 Spring 中提供了具体的实现…

通过后端代理实现Web搜索功能

大家好,才是真的好。 前面我们都在说使用Domino自带的视图搜索功能,这一篇也是,不过不是视图搜索,而是整个Notes数据库搜索,然后再将结果返回给浏览器网页呈现。 前提和前面两篇都是一样的,即Notes应用需…

Java 如何不使用 volatile 和锁实现共享变量的同步操作

前言 熟悉 Java 并发编程的都知道,JMM(Java 内存模型) 中的 happen-before(简称 hb)规则,该规则定义了 Java 多线程操作的有序性和可见性,防止了编译器重排序对程序结果的影响。 按照官方的说法: 当一个变量被多个线程读取并且至…

「数据密集型系统搭建」原理篇|数据类型不怕精挑细选

本篇围绕MySQL数据库的底层存储模型、列类型来聊聊数据库表设计及建模中要注意的事项,剖析最根源的底层物理存储文件,用最真实的数据剖析来证明和解答开发过程中的疑惑。 在一些技术谈资、面试沟通过程中,MySQL特别是我们常用的Innodb存储引擎…

JavaScript 作用域

文章目录JavaScript 作用域JavaScript 作用域JavaScript 局部作用域JavaScript 全局变量JavaScript 变量生命周期函数参数HTML 中的全局变量你知道吗?JavaScript 作用域 作用域可访问变量的集合。 JavaScript 作用域 在 JavaScript 中, 对象和函数同样也是变量。 在 JavaScr…