当前位置: 首页 > news >正文

手撕分布式缓存---多节点的调取

  经过上一个章节的学习,我们已经知晓了如何搭建了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)]]
}

相关文章:

手撕分布式缓存---多节点的调取

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

C/C++编程中的算法实现技巧与案例分析

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

干货分享 | 如何在TSMaster中对常用总线报文信号进行过滤?

TSMaster软件平台支持对不同总线&#xff08;CAN、LIN、FlexRay&#xff09;的报文和信号过滤&#xff0c;过滤方法一般有全局接收过滤、数据流过滤、窗口过滤、字符串过滤、可编程过滤&#xff0c;针对不同的总线信号过滤器的使用方法也基本相同。今天重点和大家分享一下关于T…...

k8s链接数据库故障Waiting for table metadata lock

场景&#xff1a;早上来发现一个程序&#xff0c;链接mysql数据库有点问题&#xff0c;随后排查&#xff0c;因为容器在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是一种常见的中间件&#xff0c;在项目中经常用到&#xff0c;它具有异步、解耦、削峰填谷的作用。 异步 比如下单流程&#xff0c;A服务—>B服务&#xff0c;总的耗时是A耗时时间B耗时时间&#xff0c;而改为A—>mq---->B后&#xff0c;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 磁盘基本结构 磁盘&#xff1a;柱面&#xff0c;磁道&#xff0c;扇区。 1.2 磁盘的存取过程 存取时间寻…...

1663:【 例 1】取石子游戏 1

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

Django去访问web api接口Object of type Session is not JSON serializable

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

每日一题,二维平面

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

【jupyter notebook】jupyter notebook 调用另一个jupyter notebook 的函数

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

Linux--学习记录(3)

G重要编译参数 -g&#xff08;GDB调试&#xff09; -g选项告诉gcc产生能被GNU调试器GDB使用的调试信息&#xff0c;以调试程序编译带调试信息的可执行文件g -g hello.c -o hello编译过程&#xff1a; -E&#xff08;预处理&#xff09; g -E hello.c -o hello.i-S&#xff08;编…...

自然语言处理阅读第一弹

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

Spring Boot+Mybatis设置sql日志打印

在全局配置文件添加以下内容&#xff1a;logging.level.com.demo.mapperdebug&#xff0c;com.demo.mapper&#xff1a;src下的mapper路径&#xff0c;debug&#xff1a;设置日志打印级别为debug&#xff0c;亦可设置为&#xff1a;ERROR、WARN、INFO application.properties …...

步进电机电流设置的3种方法

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

uniapp-使用返回的base64转换成图片

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

有机面条市场分析:到2026 年的复合年增长率为 5.4%

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

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...