[go 语言学习笔记] 7天用Go从零实现分布式缓存GeeCache 「持续更新中」

news2024/11/25 2:25:47

说明

本文用于记录学习 go 语言过程中的笔记, 文中的代码都是在文本中敲出来的伪代码, 并不能直接运行, 如有需要可以参考原文链接.

本文的整体思路是对原系列教程阅读后的复盘.

关于本文参考的 学习教程 可以访问原教程链接:

7天用Go从零实现分布式缓存GeeCache

本文如有侵占原文博主的权益, 还请指出, 本人尽可能及时做出调整.

笔记大纲

  • 第一天,为了解决资源限制的问题,实现了 LRU 缓存淘汰算法;

  • 第二天实现了单机并发,并给用户提供了自定义数据源的回调函数;

  • 第三天实现了 HTTP 服务端;

  • 第四天实现了一致性哈希算法,解决远程节点的挑选问题;

  • 第五天创建 HTTP 客户端,实现了多节点间的通信;

  • 第六天实现了 singleflight 解决缓存击穿的问题;

  • 第七天,使用 protobuf 库,优化了节点间通信的性能。

第一天 LRU 缓存淘汰

一、 定义缓存的存储容量以及缓存容量达到上限时的解决方案

当我们设计缓存时, 我们需要考虑到一个问题, 我们缓存容量一定要是可控的, 不能无限量存储, 因此需要定义一个最大存储容量, 这个我们定义为 maxBytes ,为了和最大存储容量进行比较, 我们还需要计算已占用的字节大小, 我们定义为 nbytes.

当我们定义完最大容量后, 我们要考虑到, 如果存储容量达到设定的最大值时, 想要继续存储数据时, 改怎么办?

有如下两种办法:

  1. 抛异常提示, 容量达到上限, 让管理员自己去清理.

    这个我们通过对外暴露接口, 让客户端调用对应增删改查的 API 就能实现.

  2. 使用缓存淘汰策略, 淘汰掉 “不需要的已缓存数据”.

    这时我们就需要考虑具体要提供哪几种缓存淘汰策略. 这里就先不多说, 我们直接采用 LRU(最近最少使用). 详情参考原文的博客.

二、定义缓存对象

定义缓存数据的数据结构, 以及 LRU 算法(这里省略思考过程, 直接说答案, 具体可参考原文连接)

巧妙结合 listmap 实现缓存数据的存储问题,以及 LRU 算法

在这里插入图片描述

定义数据结构

var ll = list.New()
var m = make(map[string]*list.Element) 

因为我们是 k-v 存储, key 的类型已经确定了, 就是 string, 然后结合上一章节的问题, 因为我们要实时统计存储大小, 所以存储的 元素 都要能够计算大小.所以我们抽象出如下接口, 定义要存储的 value:

type Value interface {
  Length() int// 存储元素的大小
}

所以我们整个要存储的元素也就定下来了:

type entry struct {
  key string
  value Value
}

综合上面的描述, 我们定义如下的 缓存 对象:

type Cache struct {
  maxBytes int64 // 最大存储容量
  nBytes int64 // 已占用的容量
  ll *list.List // 双向链表
  cache map[string]*list.Element // map
  OnEvicted func(key string, value Value) // 缓存删除后的回调方法
}

三、定义创建缓存对象的方法

func New(maxBytes int64, OnEvicted func(key string, value Value)) (c *Cache) {
  return &Cache{
    maxBytes: maxBytes,
    nBytes: 0,
    ll: list.New(),
    cache: make(map[string]*list.Element),
    OnEvicted: OnEvicted
  }
}

四、定义 LRU 缓存淘汰策略

func (c *Cache) RemoveOldest() {
	// 拿出队尾元素
  e := c.ll.Back()
  // 从队列中删除
  c.ll.Remove(e)
  entry := e.Value.(*Entry)
  // 删除缓存中的 key
  delete(c.cache, entry.key)
  // 已使用的大小 - 删除的元素大小
  c.cache.nBytes -= (entry.Value.length + len(entry.key))
  // callback
  OnEvicted(entry.key, entry.Value)
}

五、定义增删改查的方法:

// 添加或更新缓存
func (c *Cache) Add(key string, value Value) {
  if c == nil {
    panic("breaking error, cache object is nil!")
  }
  // 如果缓存已经存在
  if e, ok := c.cache[key], ok {
    // 将元素移到队首
    c.ll.MoveToFront(e)
    // 覆盖之前的值
    c.cache[key] = value
    // 减去原先的元素占用的大小
    c.nBytes -= e.Value.(*Entry).Value.Length() + len(key)
  } else {
    // 如果元素不存在, 就创建一个
    e := &entry{key, value}
  	// 放入队首
  	ll.PushFront(e)
  	// 放入缓存
  	c.cache[key] = e
  }
  
  // 要存储的元素大小
  entryLength := len(key) + value.Length()
  // 新元素进入缓存后, 重新计算已占用大小
  c.nBytes += entryLength
  // 触发缓存淘汰策略, 一直到已占用容量小于最大值
  for (c.nBytes + entryLength) > maxBytes {
    c.RemoveOldest()
  }
  
} 

// 查询缓存
func (c *Cache) Get(key String) (val Value, ok bool) {
  // 取出元素
  e := c.cache[key]
  // 如果查询到了
  if e != nil {
    // 因为成为了热点数据, 所以要往队首放
    c.ll.PushFront(e)
    return e.Value.(*Entry).Value, true
  }
}

第二天

第三天

第四天

第五天

第六天

第七天

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

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

相关文章

文本检测识别技术在合合信息的应用实务解决方案

合合信息保险行业全业务流程数字化解决方案 合合信息依托AI大数据,打造了保险行业全业务流程数字化解决方案:OCR智能分类识别文档、表格、卡证、票据、合同等,替代人工录入,图像智能质检优化,实现投保、核保、理赔、合…

(Matlab实现)基于蒙特卡洛模拟的大规模电动车充电模型

目录 摘要: 1电动车日行驶里程概率分布: 2.电动车充电起始时间概率分布: 3.大规模电动车充电行为蒙特卡洛建模: 3.1日行驶里程 3.2开始充电时间 3.3耗电量 3.4充电时间 3.5总充电负荷 4.不同规模的电动车的充电负荷曲线…

vue中vue-router安装与配置方法步骤详解

vue-router 是 vue.js 官方的路由插件,里面组件和 URL 的映射关系由 vue-route 帮我们管理。 在 vue-router 的单页面应用中,页面的路径的改变就是组件的切换。 第一步: 1.正常初始化项目的时候,会有个 vue-router 供我们选择。…

docker安装kafka、zookeeper

docker安装kafka、zookeeper 基于win10,docker desktop 基于linux也是一样的处理方式 (win10通过Docker搭建LNMP环境全流程)[https://blog.csdn.net/fendouweiqian/article/details/128062543] docker安装kafka、zookeeper 创建共享网络 为的是容器内可以通讯 …

vue-cli-3环境搭建和配置

一、vue 是单文件组件 之前注册组件有什么缺点 ? 1- 缺乏语法高亮 2-格式不好 3-没有专门的写css代码等等 参考 : vue > 工具 > 单文件组件 什么是单文件组件 :后缀为 .vue 的文件 单文件组件的三个组成部分 (代码块 : scaffold 自动提示) template (模…

21. 合理的模型初始化和激活函数

1. 让训练更加稳定 2. 让每层的方差是一个常数 以两个变量为例,均值为零可以让变量于自己的轴对称,那么在二维上整个变量分布就是中心对称,而方差则可以控制各个变量离原点的离散程度,那么就可以把二维变量看成限制在某个圈内。 在…

物联网开发笔记(61)- 使用Micropython开发ESP32开发板之控制3.2寸触摸屏的SD卡(续)

一、目的 这一节我们学习如何使用我们的ESP32开发板来控制3.2寸触摸屏的SD卡。 关键字:3.2寸SPI串口TFT液晶显示屏模块 ILI9341驱动 LCD触摸屏 240*320 XPT2046触摸屏芯片IC 二、环境 ESP32 3.2寸触摸屏SD卡模块 Thonny IDE 几根杜邦线 接线方法: …

[附源码]计算机毕业设计的中点游戏分享网站Springboot程序

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: Springboot mybatis MavenVue等等组成,B/S模式…

一篇文章带你深入了解JavaScript中let+var的特性

暂时性死区 上篇文章我们了解到了let和var之间的细微差别&#xff0c;同时它们之间还有一个区别就是let声明的变量不会在作用域中被提升 <script> //name会被提升console.log(name); //undefinedvar name "Matt"; ​//age不会被提升console.log(age); //…

5.Naocs系列之集群部署

本文学习nacos基于docker的集群部署 1. 新增mysql8 Dockerfile文件 // online/shenjian/nacos/nacosCluster/image/mysql FROM mysql:8.0.30 ADD https://raw.githubusercontent.com/alibaba/nacos/develop/distribution/conf/mysql-schema.sql /docker-entrypoint-initdb.d/…

极客时间Kafka - 13 Kafka 中的高水位和 Leader Epoch 机制

文章目录1. 什么是高水位&#xff1f;2. 高水位的作用3. 高水位更新机制1. Leader 副本高水位更新机制2. Follower 副本高水位更新机制4. 副本同步机制解析5. Leader Epoch你可能听说过高水位&#xff08;High Watermark&#xff09;&#xff0c;但不一定耳闻过 Leader Epoch。…

vxe-table 实现表格填写自动计算且限制数值的复杂操作

vxe-table 实现表格填写自动计算且限制数值复杂操作 效果 演示思路 输入框设置最大值 :max"row.max"输入框调用方法blur"updateFooterEvent(row, $event)" input" if (row[item.field] > row.max) { row[item.field] row.max; } " 上代码…

我们是如何追逐元宇宙、XR等“概念股”浪潮的?

作者&#xff1a;金擘(渚薰) 1.今年我们迈出的第一步 随着淘宝人生小屋项目的正式上线&#xff0c;淘宝人生今年的元宇宙规划初步成型。 加上在 S1 同淘宝直播团队的合作上线的 Disney 毛毛狂欢馆&#xff0c;我们也正式迈出了“元宇宙”技术的第一步。 今年是淘宝人生上线 3…

数智技术,企业绿色低碳转型的催化剂?大咖说新一期每周推荐来袭!

“双碳”战略下&#xff0c;企业会遇到哪些机遇和挑战&#xff1f;中小企业如何实现绿色转型&#xff1f;数字化又在其中发挥了怎样的作用&#xff1f; 介绍&#xff1a; 阿里云能耗云总经理 周文闻 施耐德电气首席数字化设计师 毛春景 “双碳”战略下&#xff0c;企业会遇到…

你写过哪些有趣的python?(附零基础学习资料)

前言 某网站上有个浏览超过400万的问题&#xff1a;可以用 Python 编程语言做哪些神奇好玩的事情&#xff1f; 我先举一个很不专业的栗子… 然后再找几个人抬一堆例子来… 不是很稀饭《复联》嘛&#xff0c;看了《复联4》&#xff0c;就用50行Python代码做了这些&#xff1a;…

Word控件Spire.Doc 【超链接】教程(4):如何修改Word文档中的超文本

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

【leetcode】从前序与中序遍历序列构造二叉树

一、题目描述 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例1&#xff1a; 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出:…

rk3399-uboot2017-增加串口通信

一、主要还是解决这个问题&#xff0c;最后没办法&#xff0c;只能进行串口通信&#xff0c;来识别屏幕的类型了。 基本步骤如下&#xff1a; 1. uboot阶段发送串口指令&#xff0c;等待串口回复数据&#xff0c;根据数据识别屏幕类型 2.在bootargs增加一个自定义的参数&…

一个完整的项目是如何开展的:对项目生涯的总结

我正在参加「掘金启航计划」 变量的类型指的是变量的特性或特征&#xff0c;比如表示数字类型、文本类型、集合类型等&#xff0c;表示的是一类数据。 Dart提供以下的内置类型&#xff1a; 数字&#xff1a;int, double (整型(表示整数)&#xff0c;浮点型(表示小数))布尔&a…

程序员如何平衡主业和副业?

什么是副业&#xff1f; 对于副业的定义&#xff0c;每个人都有自己的见解。不过对大部分人来说&#xff0c;副业相对于主业而言&#xff0c;就是工作日朝九晚五的工作称为主业&#xff0c;其他时间做的赚钱的项目或者工作可以称之为副 副业可以是写文章、拍视频、打代码&…