【go项目-geecache】动手写分布式缓存 - day1 - 实现LRU算法
【go项目-geecache】动手写分布式缓存 - day2 - 单机并发缓存
【go项目-geecache】动手写分布式缓存 - day3 - HTTP 服务端
【go项目-geecache】动手写分布式缓存 - day4 - 一致性哈希(hash)
【go项目-geecache】动手写分布式缓存 - day5 - 分布式节点
【go项目-geecache】动手写分布式缓存 - day6 - 防止缓存击穿
【go项目-geecache】动手写分布式缓存 - day7 - 使用 Protobuf 通信
一 :实现LRU算法
做了什么?
这里我们定义了数据结构 Cache 包含一个双向链表和map,map表示真正的缓存,双向链表表示最近的缓存使用情况,然后定义一个entry,用来淘汰双向链表队首,根据key删除map数据
我的收获:
- 学会了接口类型转化 A. (type),意思时把A接口转换成type类型
- 学会了go的单元测试
- 回调函数的定义
二 :单机并发缓存
做了什么?
- 定义了数据结构ByteView用来表示缓存值,里面使用byte可以表示任意数据类型如图片
- 定义cache封装lru.Cache,包括一个Mutex锁,Cache缓存主体,缓存大小,目的是添加了并发机制
- 封装增删的方法,添加并发
- 定义接口 Getter 和 回调函数
Get(key string)([]byte, error)
,用来在缓存不存在时,从外界获取数据 - 定义数据结构Group封装了cache,目的是负责与用户的交互,并且控制缓存值存储和获取的流程,里面包含name,表示缓存空间命名,主体cache,和回调函数getter
- 实现Group的Get,new方法
我的收获:
- 学会并发控制,锁的应用
- 接口型函数的应用场景
- 数据结构之间的封装
- defer的用法
三 :HTTP服务端
做了什么?
- 定义 HTTPPool ,作为HTTP通信的主要数据结构,包括self自己名字和basepath前缀名字
- 实现了Log记录日志,ServerHTTP,先判断是不是合法通信格式,然后完成服务端功能即是缓存找不到时,从外界获取数据
我的收获:
- 了解了HTTP通信
四 :一致性哈希
做了什么?
- 定义数据结构Map实现一致性哈希算法,包含哈希函数,虚拟节点倍数
replicas
(为了进一步提高系统的可靠性和负载均衡,可以将每个物理节点映射到多个虚拟节点),哈希环key(存下所有key),映射表hashMap
- 实现增删接口
我的收获:
- 如何实现一致性哈希
- 哈希函数的设置
- 虚拟节点的作用
五 :分布式节点
做了什么?
- 抽象PeerPicker接口,目的是实现节点选择功能,实现Get方法,首先把URL组合起来,HTTP连接,如果返回HTTP状态码200表示正常,然乎读取返回
- 为HTTPPool 添加节点选择,添加peers为一致性哈希算法的map,httpGetters为 map表示远程对应的节点。
- 实现 PeerPicker 接口,Set()和PickPeer()
- 集成在主流程
我的收获:
- 加深了锁的使用,涉及修改就要用
- 时刻要考虑不安全的情况
六:防止缓存击穿
做了什么?
- 创建call表示正在进行或已经结束的请求,包括一个sync.WaitGroup(等待组),空接口val,创建Group,管理不同的key请求,包括互斥锁,map表示不同请求的状态
- 实现DO方法,保证对于每一个请求只会执行一次函数
- 在Group加入singleflight
收获
- 空接口和匿名函数的使用
- 缓存击穿/崩溃/穿透的概念
- 如何防止缓存击穿
七 :Protobuf 通信
做了什么?
- 定义.proto文件,包括基本的信息结构,还有一个RPC方法名,它接受一个类型为 “Request” 的参数,并返回一个类型为 “Response” 的响应。
- 利用
protoc --go_out=. *.proto
自动生成geecachepb.pb.go
- 修改PeerGetter ,该改为使用
geecachepb.pb.go
中的数据类型 - 修改group.go所有使用了
PeerGetter
接口的地方
收获
- 加深了对HTTP通信的理解
- 了解了Protobuf通信