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

手撕分布式缓存---HTTP Client搭建

  经过上个章节的学习,我们已经实现了一致性哈希算法,这个算法保证我们可以在节点发生变动时,最少的key请求受到影响,并返回这个节点的名称;这很大程度上避免了哈希雪崩和哈希穿透的问题。这个章节我们要基于此实现完整的服务器端在处理客户端请求时,内部如何进行选择节点,并从此节点中找到key-value


前文链接

手撕分布式缓存之一 | 定义缓存结构体与实现底层功能函数
手撕分布式缓存之二 | 互斥锁的优化
手撕分布式缓存之三 | HTTP Server搭建
手撕分布式缓存之四 | 多节点的调取策略


由于战线拉的太长了,导致后面几个章节有点失去了热情,因此就不复现代码了,采用人工理解+AI注释的方式记录

系列目录

  • (1)多节点情况下的交互
    • (1.2)原理讲解
    • (1.3)代码注释
  • (2)防止缓存击穿
    • (2.1)缓存常有的三种问题
    • (2.2)缓存击穿的解决方案
    • (2.3)代码注释
  • (3)引入Protobuf优化服务性能

(1)多节点情况下的交互

(1.2)原理讲解

  当我们有多个缓存节点时,请求key发送时应该发送给谁,例如Redis这种分布式缓存采用的方法均是:客户端发送请求时是随机发送的;接收的服务端也不一定就存有这个key-value,但他会先检查本地的缓存是否存有这个key-value,如果没有,服务器端会通过一致性hash算法计算应该去哪个节点上去查询,并去调用对应服务器端的查询接口,获取到数据后统一返回给客户端,不再让客户端去调取。

  当新的节点加入后,需要广播通知其他节点自己的存在,如果是非主从复制节点关系的情况下,由于一致性hash算法,原本需要查看节点B才可以获取到的key-value现在需要通过新增的节点A去获取,如果当时节点B的压力过大,那么可以在请求查询B查询不到时,通过节点B获取的key-value逐步的存储在节点A,以实现符合一致性hash的预期;如果当时节点B的压力并不大,那么可以直接通过计算,查询出节点B的哪些key现在会被定位到节点A,由节点B主动的将数据同步给节点A。如果是删除节点也是同理。

(1.3)代码注释

package geecacheimport ("errors""fmt""net/http""strings""github.com/gorilla/mux"
)// httpGetter 结构体实现了 PeerGetter 接口,并使用 HTTP GET 方法从指定 URL 检索数据。
//如果没有错误,则返回字节数组;否则返回错误。
type httpGetter struct {baseURL string // baseURL 是该对等机的基本 URL(协议 + IP地址 + 端口)
}func (h *httpGetter) Get(key string) ([]byte, error) {resp, err := http.Get(h.baseURL + "/cache/" + key) // 向给定的 URL 发出 GET 请求if err != nil {return nil, fmt.Errorf("HTTPPool: Error fetching %s from peer: %v", key, err)}defer resp.Body.Close()if resp.StatusCode == http.StatusNotFound {return nil, errors.New("HTTPPool: Key not found")} else if resp.StatusCode != http.StatusOK {return nil, fmt.Errorf("HTTPPool: Peer returned HTTP status code %d", resp.StatusCode)}body, err := io.ReadAll(resp.Body) // 从响应中读取数据if err != nil {return nil, fmt.Errorf("HTTPPool: Error reading response body: %v", err)}return body, nil
}// 这是一个简单的 Getter,用于检索来自对等机(通过 HTTP)的键值。如果未找到该键或发生错误,则返回错误。 
// func (h *httpGetter) Get(key string) ([]byte, error) { 
// 向给定 URL 发出 GET 请求 resp, err := http.Get(h.baseURL + "/cache/" + key)

(2)防止缓存击穿

在这里插入图片描述

(2.1)缓存常有的三种问题

  1. 缓存雪崩:缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。缓存雪崩通常因为缓存服务器宕机、缓存的 key 设置了相同的过期时间等引起。
  2. 缓存击穿:一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到 DB ,造成瞬时DB请求量大、压力骤增。
  3. 缓存穿透:查询一个不存在的数据,因为不存在则不会写到缓存中,所以每次都会去请求 DB,如果瞬间流量过大,穿透到 DB,导致宕机。

三者的直接原因均是缓存中的key失效,请求只能通过查询DB才能够获取key-value,但是它们出现的场景和解决方式不同,这才是我们区分三者的方式。

(2.2)缓存击穿的解决方案

  缓存击穿的本质问题是:当存在一个时刻存在key失效后,有大量的key请求同时发送,且都落在了DB上。 针对这一问题,我们无法限制客户端同时发送大量key这一问题,但是我们可以限制当请求没有在缓存阶段找到key-value时,只有一个请求可以落到DB上。例如:当有大量的请求进行访问,我们可以通过互斥锁的方式进行限制,比如我们先判断如果缓存中存在key-value,那么可以直接返回结果,也不会造成缓存击穿的影响;但如果在缓存中找不到对应的key-value,那么我们可以允许第一个请求此不存在的key进行接下来的操作(DB操作),其他的请求则需要进行等待,等到唯一的一个请求处理结束之后,该对应key的互斥锁会打开,之后等待的请求直接返回结果即可。也就是说我们可以声明一个map,这个map的key是缓存中的key,map的value是一个对象,这个对象不仅可以表示当前的key是否已经有请求进行访问(是不是已经被锁定),也可以存储获取此key的第一个请求获取到的value值或异常信息。

(2.3)代码注释

type call struct {wg sync.WaitGroup // WaitGroup用于同步等待所有goroutine完成任务后再返回结果val interface{} // 保存函数fn()的返回值err error      // 保存函数fn()的错误信息
}type Group struct {mu sync.Mutex // 互斥锁,保证map操作的原子性m map[string]*call   
}func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) { // 传入一个字符串类型的key和接收两个参数的函数指针fn,返回一个interface{}类型的值和error类型的错误信息g.mu.Lock()if g.m == nil { // 如果map为空则初始化mapg.m = make(map[string]*call)}if c, ok := g.m[key]; ok { // 判断是否已经有正在运行或者等待中的goroutine处理该任务g.mu.Unlock()     // 解锁以防止死锁c.wg.Wait()      // 等待任务完成后获取结果return c.val, c.err // 返回结果}c := new(call)   c.wg.Add(1)    // WaitGroup加1,表示新增一个需要等待的goroutineg.m[key] = c  // 将当前任务添加到map中,用于标记正在执行或者等待中的任务g.mu.Unlock()go func() {defer c.wg.Done() // 函数执行完成后,WaitGroup减1c.val, c.err = fn() // 执行传入的fn函数并保存值和错误信息}()g.mu.Lock()delete(g.m, key) // 从map中删除已经完成的任务g.mu.Unlock()return c.val, c.err // 返回结果
}

(3)引入Protobuf优化服务性能

简单看了下介绍,个人理解Protobuf是非常值得学习的一门技术,对于服务性能的优化有很大的作用,准备深入了解一下,然后再完善这一部分,感兴趣的同学可以留个眼,更新之后一一通知。

相关文章:

手撕分布式缓存---HTTP Client搭建

经过上个章节的学习,我们已经实现了一致性哈希算法,这个算法保证我们可以在节点发生变动时,最少的key请求受到影响,并返回这个节点的名称;这很大程度上避免了哈希雪崩和哈希穿透的问题。这个章节我们要基于此实现完整的…...

word如何快速制作简易代码块

先上解决方案。 方式一(全自动): typora编辑,导出选择word文档即可。内网环境,故放弃。 方式二(全手动): 在修改文档时,左侧会有“段落布局”按钮,点击该按…...

Linux常用网络指令

网络参数设定使用的指令 手动/自动设定与启动/关闭 IP 参数&#xff1a;ifconfig, ifup, ifdown ifconfig ifconfig常用于修改网络配置以及查看网络参数的指令 [rootwww ~]# ifconfig {interface} {up|down} < 观察与启动接口 [rootwww ~]# ifconfig interface {options…...

Spark on Yarn 安装配置实验(3.1.1)

子任务二: Spark on Yarn 安装配置 本任务需要使用 root 用户完成相关配置, 已安装 Hadoop 及需要配置前置环境,具体要求如下: 1 、从宿主机 /opt 目录下将文件 spark-3.1.1-bin-hadoop3.2.tgz 复制到容器 Master 中的 /opt/software (若 路径不存在,则需新…...

详解YOLOv5网络结构/数据集获取/环境搭建/训练/推理/验证/导出/部署

一、本文介绍 本文给大家带来的教程是利用YOLOv5训练自己的数据集&#xff0c;以及有关YOLOv5的网络结构讲解/数据集获取/环境搭建/训练/推理/验证/导出/部署相关的教程&#xff0c;同时通过示例的方式让大家来了解具体的操作流程&#xff0c;过程中还分享给大家一些好用的资源…...

ansible(不能交互)

1、定义 基于python开发的一个配置管理和应用部署工具&#xff0c;在自动化运维中异军突起&#xff0c;类似于xshell一键输入的工具&#xff0c;不需要每次都切换主机进行操作&#xff0c;只要有一台ansible的固定主机&#xff0c;就可以实现所有节点的操作。不需要agent客户端…...

黑马点评06分布式锁 2Redisson

实战篇-17.分布式锁-Redisson功能介绍_哔哩哔哩_bilibili 1.还存在的问题 直接实现很麻烦&#xff0c;借鉴已有的框架。 2.Redisson用法 3.Redisson可重入原理 在获取锁的时候&#xff0c;看看申请的线程和拿锁的线程是否一致&#xff0c;然后计算该线程获取锁的次数。一个方法…...

深度剖析知识图谱:方法、工具与实战案例

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 知识图谱作为一种强大的知识表示和关联技术&am…...

Oracle中的dblink简介

Oracle中的dblink简介 是一种用于在不同数据库之间进行通信和数据传输的工具。它允许用户在一个数据库中访问另一个数据库中的对象&#xff0c;而无需在本地数据库中创建这些对象。 使用dblink&#xff0c;用户可以在一个数据库中执行SQL语句&#xff0c;然后访问另一个数据库中…...

ubuntu安装显卡驱动过程中遇到的错误,及解决办法!

ubuntu安装显卡驱动的过程中&#xff0c;可能会遇到以下问题&#xff0c;可以参考解决办法&#xff01; 问题1&#xff1a; ​ ERROR: An error occurred while performing the step: "Building kernel modules". See /var/log/nvidia-installer.log for details. …...

【程序】STM32 读取光栅_编码器_光栅传感器_7针OLED

文章目录 源代码工程编码器基础程序参考资料 源代码工程 源代码工程打开获取&#xff1a; http://dt2.8tupian.net/2/28880a55b6666.pg3这里做了四倍细分&#xff0c;在屏幕上显示 速度、路程、方向。 接线方法&#xff1a; 单片机--------------串口模块 单片机的5V-------…...

TestSSLServer4.exe工具使用方法简单介绍(查SSL的加密版本SSL3或是TLS1.2)

一、工具使用方法介绍 工具使用方法参照&#xff1a;http://www.bolet.org/TestSSLServer/ 全篇英文看不懂&#xff0c;翻译了下&#xff0c;能用到的简单介绍如下&#xff1a; 将下载的TestSSLServer4.exe工具放到桌面上&#xff0c;CMD命令行进入到桌面目录&#xff0c;执…...

新年跨年烟花超酷炫合集【内含十八个烟花酷炫效果源码】

❤️以下展示为全部烟花特效效果 ❤️下方仅展示部分代码 ❤️源码获取见文末 🎀HTML5烟花喷泉 <style> * {padding:0;margin:0; } html,body {positi...

计算机网络考研辨析(后续整理入笔记)

文章目录 体系结构物理层速率辨析交换方式辨析编码调制辨析 链路层链路层功能介质访问控制&#xff08;MAC&#xff09;信道划分控制之——CDMA随机访问控制轮询访问控制 扩展以太网交换机 网络层网络层功能IPv4协议IP地址IP数据报分析ICMP 网络拓扑与转发分析&#xff08;重点…...

JMESPath语言

JMESPath&#xff08;JSON Matching Expression Path&#xff09; 一种查询语言。 主要用于从JSON文档中检索和过滤数据。 通过写表达式提取和处理JSON数据&#xff0c;而无需编写复杂的代码。 功能&#xff1a;数据提取、过滤、转换、排序。 场景&#xff1a;处理API响应…...

【C++高阶(七)】C++异常处理的方式

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 异常处理的方式 1. 前言2. C语言处理异常的方式…...

在Idea中创建基于工件的本地服务

目录 1、创建基于工件的Tomcat服务器&#xff1a; 2、修改名称&#xff1a; 3、修改服务器项&#xff1a; 4、部署项 5、最后记得点右下角的【应用】和【确定】保存。 1、创建基于工件的Tomcat服务器&#xff1a; 运行->编辑配置->【Tomcat服务器】->本地 2、修…...

十六、YARN和MapReduce配置

1、部署前提 &#xff08;1&#xff09;配置前提 已经配置好Hadoop集群。 配置内容&#xff1a; &#xff08;2&#xff09;部署说明 &#xff08;3&#xff09;集群规划 2、修改配置文件 MapReduce &#xff08;1&#xff09;修改mapred-env.sh配置文件 export JAVA_HOM…...

自己动手写编译器:语法解析的基本原理

在前面系列章节中我们完成了词法解析。词法解析的基本任务就是判断给定字符串是否符合特定规则&#xff0c;如果符合那么就给这个字符串分配一个标签(token)。词法解析完成后接下来的工作就要分配给语法解析&#xff0c;后者的任务就是判断一系列标签的组合是否符合特定规范。 …...

VS Code解决乱码

在上边搜索栏输入“>Change File Encoding”&#xff0c;更改编码格式&#xff0c;解决乱码格式。 VS Code会帮助确认编码格式&#xff0c;然后选择就好。 最后完成如下&#xff1a;...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...