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

news2024/10/6 1:08:01

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

import (
    "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.ChecksumIEEE
        m.hash = crc32.ChecksumIEEE
    }
    return m
}

实现节点的添加

  • strconv.Itoa(i)是将任何数字都可以转换成字符串,而强制类型转换是只能将整数转换成字符串。
  • sort.Ints(m.keys)对整形数据列表m.keys进行升序排列。
func (m *Map) Add(keys ...string) {
	// 循环每个key,对其进行复制并计算哈希值存入hashMap
    for _, 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] >= hash
        return m.keys[i] >= hash
    })

	return m.hashMap[m.keys[idx%len(m.keys)]]
}

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

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

相关文章

win环境下启动kafka Port already in use: 6688; nested exception is

背景 zk启动成功后&#xff0c;接下来启动kafka&#xff0c;再启动kafka后一直说端口被占用。 端口占用解决办法: netstat -aon|findstr 9092 taskkill -f -pid 7780 杀掉后&#xff0c;再次启动kafka时&#xff0c;问题并未解决 后来修改了批处理文件kafka-run-class.bat中…

验证码:防范官网恶意爬虫攻击,保障用户隐私安全

网站需要采取措施防止非法注册和登录&#xff0c;验证码是有效的防护措施之一。攻击者通常会使用自动化工具批量注册网站账号&#xff0c;以进行垃圾邮件发送、刷量等恶意活动。验证码可以有效阻止这些自动化工具&#xff0c;有效防止恶意程序或人员批量注册和登录网站。恶意程…

RK3568 android11 调试mipi摄像头 gc2093

一&#xff0c;摄像头简介 GC2093是一个高质量的1080P CMOS图像传感器&#xff0c;用于安全相机产品、数码相机产品和手机相机应用程序。包含了一个1920H x 1080V像素阵列、片上10位ADC和图像信号处理器。高性能和低功耗功能的全面集成使GC2093最适合设计&#xff0c;减少了实…

【05】GeoScene海图或者电子航道图批量出图

出单张000数据参考上一篇博客&#xff0c;如果想同时出多张海图000数据&#xff0c;也是可以实现的。思路如下&#xff1a; 1 批量创建产品 GeoScene海事模块通过ProductDefinitions表和ProductCoverage要素类定义产品和AOI覆盖区&#xff0c;可支持批量导入产品信息和AOI覆盖…

PFA烧杯可高温加热ICP-MS实验室适用耐强酸碱本底纯净

聚四氟&#xff08;PFA&#xff09;烧杯可用于痕量分析、同位素分析等实验&#xff0c;ICP-MS实验室适用。半导体、多晶硅、光伏电子 锂电池行业均适用。杯体刻度清晰&#xff0c;方便观察&#xff0c;尖嘴方便倾倒溶液。 技术参数 品名 规格 材质 耐受温度 PFA烧杯 10ml…

ASP.NET Core MVC依赖注入理解(极简个人版)

依赖注入 文献来源&#xff1a;《Pro ASP.NET Core MVC》 Adam Freeman 第18章 依赖注入 1 依赖注入原理 所有可能变化的地方都用接口在使用接口的地方用什么实体类通过在ConfigureService中注册解决注册的实体类需要指定在何种生命周期中有效 TransientScopedSingleton 2…

开源学习项目推荐

文章目录 koodo-reader凤凰架构学习项目NPS 内网穿透客户端 koodo-reader 项目地址&#xff1a;https://github.com/koodo-reader/koodo-reader 介绍&#xff1a;一个开源的阅读器&#xff0c;阅读pdf也有目录&#xff0c;作为epub阅读器和pdf阅读器看资料挺好 凤凰架构 项…

NPDP证书含金量高吗?跟PMP相比含金量怎么样?

两个证方向不太一样&#xff0c;含金量都挺高的&#xff0c;具体怎么选呢&#xff1f;接着往下看~ PS&#xff1a;不想看长篇大论的&#xff0c;来找我&#xff0c;直接把你的经历甩出来&#xff0c;我帮你判断~ 一、产品经理跟项目经理的区别 表面上&#xff0c;项目经理和产…

WEB渗透—PHP反序列化(四)

Web渗透—PHP反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩…

7+单细胞+分型+机器学习,最近大热的生信思路,要拿分趁现在

今天给同学们分享一篇生信文章“Machine learning-based integration develops a neutrophil-derived signature for improving outcomes in hepatocellular carcinoma”&#xff0c;这篇文章发表在Front Immunol期刊上&#xff0c;影响因子为7.3。 结果解读&#xff1a; 单细…

GaN图腾柱无桥 Boost PFC(单相)九-EMI 滤波器容性电流影响分析

前言 为了防止 PFC 变换器中高频开关谐波对电网产生影响&#xff0c;同时抑制电网中的高频干扰对变换器运行的影响&#xff0c;一般通过在 PFC 变换器与交流电源之间加入EMI 滤波器消除共模干扰和差模干扰&#xff0c;使变换器满足相应的 EMI 标准。在基于GaN 功率器件的图腾柱…

A01、深入了解性能优化

1、常用性能评价/测试指标 1.1、响应时间 提交请求和返回该请求的响应时间之间使用的时间&#xff0c;一般比较关注平均响应时间。常用操作的响应时间列表&#xff1a; 操作响应时间打开一个站点几秒数据库查询一条记录&#xff08;有索引&#xff09;十几毫秒机械磁盘一次寻…

三菱PLC FX3U滑动平均值滤波

三菱PLC滑动平均值滤波其它相关写法,请参考下面文章链接: https://rxxw-control.blog.csdn.net/article/details/125044013https://rxxw-control.blog.csdn.net/article/details/125044013滑动平均值滤波程序总共分为三部分,第一步为:滑动采样。 第二步为:队列求和,第三…

坐标前后限制转点的坐标取值+网络流拆维拆点:agc031_e

https://vj.imken.moe/contest/598718#problem/J 观察到数据范围很小&#xff0c;但一个很重要的信息我们缺失了&#xff0c;就是珠宝的数量&#xff0c;所以我们考虑枚举珠宝的数量 k k k。 对于横纵坐标什么至多至少的限制&#xff0c;比如 a i a_i ai​ 前最多偷 b i b…

openwrt docker nginx 站点搭建

应为家里一直是 openwrt 软路由&#xff0c;这样以来也不用 重新买服务器了&#xff0c;就直接在 openwrt 上面跑个 nginx就行了。把自己的一些东西就可以放上面了。资源再利用哈哈&#xff1b; 先 ssh 连接上 openwrt &#xff1a;我这里的 openwrt 最近刚更新的固件&#xff…

confluence 备份与恢复

备份 confluence 每天会自动备份文件到 /var/atlassian/application-data/confluence/backups 新增定时任务&#xff0c;每天将备份的文件拷贝到远程服务器: crontab -l 0 0 3 * * ? sh /var/atlassian/application-data/confluence/backups/backup.sh#!/bin/shscp_linux_pa…

Ansys Speos SSS|传感器特性与EMVA1288标准以及Lumerical传感器验证

附件下载 联系工作人员获取附件 概述 本文是Speos Sensor System exporter&#xff08;SSS&#xff09;的使用指南&#xff0c;这是一个强大的解决方案&#xff0c;用于camera sensor模拟结果的后处理。本文介绍了一组实际示例&#xff0c;以演示该工具基于EMVA 1288标准从传…

鸿蒙OS:打破界限的操作系统新星

导言 鸿蒙OS&#xff08;HarmonyOS&#xff09;是华为公司为应对技术封锁而推出的分布式操作系统&#xff0c;其背后蕴含着华为构建全球数字生活愿景的雄心。本文将深入剖析鸿蒙OS的起源、核心特性&#xff0c;并展望其未来在数字生态中的角色。 1. 背景与起源 华为的…

Ubuntu 常用命令之 sed 命令用法介绍

sed是一个在Linux和其他Unix-like系统中常用的流编辑器&#xff0c;用于对输入流&#xff08;文件或管道&#xff09;进行基本的文本转换。它可以非常方便地进行文本替换、插入、删除等操作。 sed命令的基本格式为 sed [options] command file(s)其中&#xff0c;常用的参数有…

【回溯】【回文字符串】131.分割回文串

题目 法1&#xff1a;DFS双指针 必须掌握基础方法&#xff01; 注意&#xff1a;使用ArrayList删除尾元素比LinkedList要快很多&#xff01;&#xff01;&#xff01; class Solution {public List<List<String>> partition(String s) {List<List<String&…