手撕分布式缓存---多节点的调取
经过上一个章节的学习,我们已经知晓了如何搭建了HTTP Server,通过HTTP协议与我们定义的路由,我们可以远程访问这个节点;基于这一点,我们可以部署多台实现了HTTP的缓存服务从而实现分布式的特性。这一章节我们要基于此背景下实现分布式缓存的前置条件:多节点下的调取。
前文链接
手撕分布式缓存之一 | 定义缓存结构体与实现底层功能函数
手撕分布式缓存之二 | 互斥锁的优化
手撕分布式缓存之三 | HTTP Server搭建
系列目录
- (1)多节点的调取
- (1.1)前言
- (1.2)一致性哈希算法的讲解与优化
- (1.3)多节点的添加与获取
- (1.4)代码实现逻辑
(1)多节点的调取
(1.1)前言
当服务使用多节点运行时,节点之间的负载均衡往往是我们优先需要考虑的问题;但缓存服务不同于一般的多节点服务,它并非仅仅是通过分摊流量就可以避免某个节点不会出问题,因为缓存往往对应着缓存雪崩的问题。举个例子:如果一个热点key在节点A中存储着,但是之后的大量该热点key的请求并没有都发送到节点A上,这样做会导致两个问题:① 每个被请求的节点都需要存储此键值对,造成了不必要的空间损失 ② 通常情况下缓存中可能存储的key-value对应着用户的Cookie,大量的请求失效导致数据库访问激增,可能导致数据库服务的崩坏。 因此我们应该主动的判断要请求的key存储在哪个节点中,尽量减少缓存穿透的情况出现。
(1.2)一致性哈希算法的讲解与优化
假设这样一个场景:我们根据节点的名称计算出对应的hashKey,并根据此value进行排序,在通过key进行查找value时,我们直接通过取余的方式进行确定应该从哪个节点中HashSearch。这样的方式看起来是行得通的,但是节点是动态的,如果我们加入了一个节点或者删除了一个节点,会导致在key进行取余定位节点时大量的对应关系失效,从而导致缓存雪崩。这种影响是致命的,每一个key值都会受到影响,只有极个别的key值可能重新计算后仍然映射到原本的关系上,但是大部分的key值会在节点变动之后找不到映射关系。
我们可以发现,上面的影响出现的原因本质上是因为key的映射关系是与节点的数量绑定的,如果我们将key-节点的映射算法抽离节点的数量就可以避免上面场景对应问题的发生了。一致性算法就达到了这样的效果,一致性算法依旧是将key使用固定的hash算法计算,但是将key映射到节点上时并不是采用取模的方式,而是去寻找最大的比hashKey小的节点;这样做的好处就是当有节点A发生变动后,收到影响的key只有那些key-hash是大于节点A对应的hash,且小于另一个节点时,它们去找到的节点会发生变化。
但是仅这样将真实的节点计算为hash也会有一个问题:哈希倾斜。意思时:如果大部分节点的hash值都很大,而只有一个节点的hash值很小,那么会有很多的key选择存储在这个hash值很小的节点中,因此当这个节点发生变动时,会引起哈希雪崩。针对这一问题,我们可以因为虚拟节点,这些选择虚拟节点的key最终仍然会存储到某一个真实节点中,这样就会使得节点的分布更加的密集,避免将一个大范围的key都映射到一个节点上。
(1.3)多节点的添加与获取
添加节点时,我们可以使用节点的唯一性标识作为key值传入,然后对节点的唯一性标识计算hash值,然后将此值存储节点的hashKey的列表中(将列表中的值排序后,便于查找hashKey对应的节点),并且将key-value存入字典中(用于在key选择节点之后,根据节点的hashKey查找对应节点的名称);值得注意的是,我们的虚拟节点的key值定义需要根据实际情况变化,最理想的情况是将所有节点(包含虚拟节点和真实节点)均匀的映射到hashMap中,这里的均匀分布不只是物理上节点排列的均匀,更好的情况是实际key请求时,存储在真实节点上的key也是均匀的。本示例中因为没有实际情况可以测试,因此简单的将不同的自然数拼接在真实节点的唯一性标识中,以标识该真实节点对应的不同的虚拟节点的唯一性标识。
获取节点的场景发生在有key进行请求时,在我们按照相同的算法将key计算为hashKey后,在存储节点hashKey的列表中寻找最大的小于此请求hashKey的节点,找到这个节点的下标后,在列表中找到节点对应的hashValue,然后在字典中找到此hashValue对应的真实节点的名称,并且返回该名称。
(1.4)代码实现逻辑
定义Hash函数的传参与响应类型,定义Map结构与实现初始化函数
- type Hash func(data []byte) uint32,表示传参类型是byte类型的列表,转换成uint32类型。
- crc32.ChecksumIEEE是包hash/crc32下的一个将string类型计算hash的函数。
package consistenthashimport ("hash/crc32""sort""strconv"
)// Hash 定义了一个将字节数组映射到uint32的函数类型。
type Hash func(data []byte) uint32// Map 结构体包含所有已经计算过哈希值的key列表,并且使用map存储每个虚拟节点对应的真实key。
type Map struct {hash Hash // 指向具体的哈希算法的函数指针replicas int // 每个真实key生成的虚拟节点数量keys []int // Sorted, 按照从小到大排序hashMap map[int]string
}// New 创建一个新的Map实例。如果没有传入Hash参数,则默认使用crc32.ChecksumIEEE作为哈希算法。
func New(replicas int, fn Hash) *Map {m := &Map{replicas: replicas,hash: fn,hashMap: make(map[int]string),}if m.hash == nil { // 如果未提供hash函数,则使用crc32.ChecksumIEEEm.hash = crc32.ChecksumIEEE}return m
}
实现节点的添加
- strconv.Itoa(i)是将任何数字都可以转换成字符串,而强制类型转换是只能将整数转换成字符串。
- sort.Ints(m.keys)对整形数据列表m.keys进行升序排列。
func (m *Map) Add(keys ...string) {// 循环每个key,对其进行复制并计算哈希值存入hashMapfor _, key := range keys {// 没有真正的节点概念,所以可视为虚拟节点,因此在生成哈希时使用当前索引和key组合来创建不同的哈希值for i := 0; i < m.replicas; i++ {// 将哈希值与key一起保存到hashMap中hash := int(m.hash([]byte(strconv.Itoa(i) + key))m.keys = append(m.keys, hash)m.hashMap[hash] = key}}// 对keys进行排序,以供Get()方法使用二分查找功能sort.Ints(m.keys)
}
实现通过key请求选择节点
- sort.Search(len(m.keys) 是根据第二个函数参数什么时候返回True来判断是否满足条件,当满足条件时返回当前元素的下标,如果均不满足则返回len(m.keys)。
- idx%len(m.keys)对idx做取余操作,以实现将hash-value环状的放置的效果
// Get函数获取最接近提供的key的项。
func (m *Map) Get(key string) string {if len(m.keys) == 0 { // 如果没有可用的key,返回空字符串return ""}hash := int(m.hash([]byte(key)) // 计算key的哈希值// 二分查找合适的复制品。sort.Search()函数是在sort包中定义的一个泛型搜索函数,//该函数会使用给定的判断条件进行二分查找,并返回满足条件的元素下标或者插入位置下标。//这里传入的参数len(m.keys)表示要搜索的切片长度,而后面的lambda函数则为判断条件,//当m.keys[i] >= hash时,就认为已经找到了合适的复制品。idx := sort.Search(len(m.keys), func(i int) bool {// 终止条件是m.keys[i] >= hashreturn m.keys[i] >= hash})return m.hashMap[m.keys[idx%len(m.keys)]]
}
相关文章:

手撕分布式缓存---多节点的调取
经过上一个章节的学习,我们已经知晓了如何搭建了HTTP Server,通过HTTP协议与我们定义的路由,我们可以远程访问这个节点;基于这一点,我们可以部署多台实现了HTTP的缓存服务从而实现分布式的特性。这一章节我们要基于此背…...

C/C++编程中的算法实现技巧与案例分析
C/C编程语言因其高效、灵活和底层的特性,被广大开发者用于实现各种复杂算法。本文将通过10个具体的算法案例,详细探讨C/C在算法实现中的技巧和应用。 一、冒泡排序(Bubble Sort) 冒泡排序(Bubble Sort)是一…...

干货分享 | 如何在TSMaster中对常用总线报文信号进行过滤?
TSMaster软件平台支持对不同总线(CAN、LIN、FlexRay)的报文和信号过滤,过滤方法一般有全局接收过滤、数据流过滤、窗口过滤、字符串过滤、可编程过滤,针对不同的总线信号过滤器的使用方法也基本相同。今天重点和大家分享一下关于T…...

k8s链接数据库故障Waiting for table metadata lock
场景:早上来发现一个程序,链接mysql数据库有点问题,随后排查,因为容器在k8s里面。所以尝试重启了pod没有效果 一、重启pod: 这里是几种在Kubernetes中重启Pod的方法: 删除Pod,利用Deployment重建 kubectl delete pod mypodDepl…...

数字经济如何驱动企业高质量发展? ——核心机制、模式选择与推进路径
文章目录 每日一句正能量前言核心机制信息化和智能化作为数字经济的核心机制信息化和智能化如何提升企业生产效率和管理水平数据的获取、分析和利用对企业发展的影响 模式选择电子商务模式的选择共享经济模式的选择数据驱动的业务模式选择 推进路径建设数字化基础设施培养数字化…...
机器学习——支持向量机
目录 一、基于最大间隔分隔数据 二、寻找最大间隔 1. 最大间隔 2. 拉格朗日乘子法 3. 对偶问题 三、SMO高效优化算法 四、软间隔 五、SMO算法实现 1. 简化版SMO算法 2. 完整版SMO算法 3. 可视化决策结果 六、核函数 1. 线性不可分——高维可分 2. 核函数 …...

mq的作用
使用mq优点 mq是一种常见的中间件,在项目中经常用到,它具有异步、解耦、削峰填谷的作用。 异步 比如下单流程,A服务—>B服务,总的耗时是A耗时时间B耗时时间,而改为A—>mq---->B后,A发送mq后立刻…...

AUTOSAR组织引入了Rust语言的原因是什么?有哪些好处?与C++相比它有什么优点?并推荐一些入门学习Rust语言链接等
AUTOSAR(汽车开放系统架构)是一个由汽车制造商、供应商和其他来自电子、半导体和软件行业的公司组成的全球发展伙伴关系,自2003年以来一直致力于为汽车行业开发和引入开放、标准化的软件平台。 AUTOSAR 最近宣布成立一个新的工作组,用于探索在汽车软件中使用 Rust 编程语言…...

基于PyCharm实现串口GUI编程
工具效果如下如所示 下面简单介绍一下操作流程 1.打开PyCharm软件 2.创建一个工程 3.给该工程命名 4.在main.py里面黏贴如下的代码 # This is a sample Python script. # Press ShiftF10 to execute it or replace it with your code. # Press Double Shift to search everyw…...

【1.8计算机组成与体系结构】磁盘管理
目录 1.磁盘基本结构与存取过程1.1 磁盘基本结构1.2 磁盘的存取过程 2.磁盘优化分布存储3.磁盘单缓冲区与双缓冲区4.磁盘移臂调度算法 1.磁盘基本结构与存取过程 1.1 磁盘基本结构 磁盘:柱面,磁道,扇区。 1.2 磁盘的存取过程 存取时间寻…...

1663:【 例 1】取石子游戏 1
【题目描述】 有一种有趣的游戏,玩法如下: 玩家: 2 人; 道具: N 颗石子; 规则: 1、游戏双方轮流取石子; 2、每人每次取走若干颗石子(最少取 1 颗,最多取…...

Django去访问web api接口Object of type Session is not JSON serializable
解决方案:settings.py中加入 :SESSION_SERIALIZER django.contrib.sessions.serializers.PickleSerializer 事由:Django去访问一个web api接口,两次连接之间需要通过Session()保持身份验证。 def sendCode(request): mobile jso…...

每日一题,二维平面
给你 二维 平面上两个 由直线构成且边与坐标轴平行/垂直 的矩形,请你计算并返回两个矩形覆盖的总面积。 每个矩形由其 左下 顶点和 右上 顶点坐标表示: 第一个矩形由其左下顶点 (ax1, ay1) 和右上顶点 (ax2, ay2) 定义。 第二个矩形由其左下顶点 (bx1, …...

【jupyter notebook】jupyter notebook 调用另一个jupyter notebook 的函数
总结 使用 %run 魔法命令将 Notebook 转换为py文件使用 nbimporter 库手动复制代码优点notebook最前面加上即可最基本方法就跟导入py文件一样,不会被执行一遍快缺点所有的代码都会执行一遍修改原文件就要重新转换,且 从自定义的 .py 文件中导入函数时&a…...

Linux--学习记录(3)
G重要编译参数 -g(GDB调试) -g选项告诉gcc产生能被GNU调试器GDB使用的调试信息,以调试程序编译带调试信息的可执行文件g -g hello.c -o hello编译过程: -E(预处理) g -E hello.c -o hello.i-S(编…...

自然语言处理阅读第一弹
Transformer架构 encoder和decoder区别 Embeddings from Language Model (ELMO) 一种基于上下文的预训练模型,用于生成具有语境的词向量。原理讲解ELMO中的几个问题 Bidirectional Encoder Representations from Transformers (BERT) BERT就是原生transformer中的Encoder两…...

Spring Boot+Mybatis设置sql日志打印
在全局配置文件添加以下内容:logging.level.com.demo.mapperdebug,com.demo.mapper:src下的mapper路径,debug:设置日志打印级别为debug,亦可设置为:ERROR、WARN、INFO application.properties …...

步进电机电流设置的3种方法
本文介绍步进电机电流设置的3种方法。 步进电机电流设置包括运行电流(IRun)和保持电流(IHold)2种。电机运行时需要有较大电流以保证有足够的力矩使物体运动,而停止的时候,为了减少电机发热及降低功耗&…...

uniapp-使用返回的base64转换成图片
在实际开发的时候 需要后端实时的给我返回二维码 他给我返回的是加密后的base64字符串 我需要利用这个base64转换到canvas画布上展示 或者以图片的形式展示在页面内 在canvas画布上展示 使用官方的uni.getFileSystemManager().writeFile()方法可将base64码转成的二维码显示在…...

有机面条市场分析:到2026 年的复合年增长率为 5.4%
近年来,有机面条因其健康益处和可持续性而广受欢迎。由于消费者对健康和天然食品的需求不断增加,预计 全球有机面条市场将继续以显着速度增长。特别是中国市场,由于健康意识的提高以及对有机和天然产品的兴趣 增加,有机面条消费量…...

广州设计周落幕|值得被歌颂的奥力斯特岩板
12月11日,一年一度的广州设计周,为期四天的展会在广州保利世贸博览馆、广州国际采购中心和南丰国际会展中心三大展馆已落下帷幕。依旧熙攘,依旧热烈,远道而来的专家领导、媒体嘉宾、展商代表、外国友人、设计爱好者,风…...

WTN6系列语音芯片:PWM与DAC音频输出在PCB设计中的优势
随着科技的飞速发展,语音芯片在电子产品中的应用越来越广泛。其中,唯创知音的WTN6系列语音芯片凭借其卓越的性能和多样的功能,受到了市场的热烈欢迎。特别是其支持PWM和DAC两种音频输出方式的特点,使得工程师在PCB设计时能够更加灵…...

设计模式 原型模式 与 Spring 原型模式源码解析(包含Bean的创建过程)
原型模式 原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。 工作原…...

Docker介绍,Docker安装
docker镜像仓库官网 一、Docker的基本概念 1.Docker的三大核心组件 docker 镜像 --------docker images docker 仓库---------docker registeries docker 容器---------docker containers 2.Docker 镜像 Docker镜像是运行docker容器时的只读模板,每一个镜像由一…...

CLIP 对比学习 源码理解快速学习
最快的学习方法,理清思路,找视频讲解,看源码逻辑: CLIP 源码讲解 唐宇 输入: 图像-文本成对配对的数据 训练模型的过程(自己理解): 怎么做的?:利用数据内部…...

6.鸿蒙app_hap_DevEco如何真机调试模式_app安装在手机中
真机调试 手机》设置》关于手机》HarmonyOS版本》软件版本,连续单击10次启动开发者模式 然后:设置》系统和更新》开发人员选项》打开USB调试功能。 电脑USB连接手机,手机USB连接类型,传文件(不要选择仅充电…...

【JVM从入门到实战】(八)垃圾回收(1)
内存泄漏:指的是不再使用的对象在系统中未被回收,内存泄漏的积累可能会导致内存溢出 什么是垃圾回收 Java中为了简化对象的释放,引入了自动的垃圾回收(Garbage Collection简称GC)机制。通过垃 圾回收器来对不再使用的…...

LeeCode前端算法基础100题(12)-删除有序数组中的重复项
一、问题详情: 给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题…...

MATLAB图解傅里叶变换(初学者也可以理解)
1、概述 相信很多人对于傅里叶变换可能觉得比较复杂和有点难懂,其实不难,它只是一种积分变换。 傅里叶变换,表示能将满足一定条件的某个函数表示成三角函数(正弦和/或余弦函数)或者它们的积分的线性组合。也就是说&qu…...

uni-app 用于开发H5项目展示饼图,使用ucharts 饼图示例
先下载ucharts H5示例源码: uCharts: 高性能跨平台图表库,支持H5、APP、小程序(微信小程序、支付宝小程序、钉钉小程序、百度小程序、头条小程序、QQ小程序、快手小程序、360小程序)、Vue、Taro等更多支持canvas的框架平台&#…...