-
HTTPPool既可以是服务端,也可以是客户端,这取决于特定的使用场景和上下文:- 作为客户端 :当本地缓存没有找到需要的数据时,
HTTPPool需要作为客户端,通过httpGetter(实现了PeerGetter接口)去其他的远程节点(服务端)请求数据。 - 作为服务端 :当其他节点的
HTTPPool实例(作为客户端)请求当前节点存储的数据时,当前节点的HTTPPool实例则作为服务端响应这些请求。
- 作为客户端 :当本地缓存没有找到需要的数据时,
-
HTTPPool实现了PeerPicker接口,httpGetter实现了PeerGetter接口,前者用来找到对应的远程节点(由于每个远程节点对应一个httpGetter,因此实际找到的是httpGetter),后者用来向远处节点请求数据 -
Peer.gopackage gee type PeerPicker interface { PickPeer(key string) (peer PeerGetter, ok bool) } type PeerGetter interface { Get(group string, key string) ([]byte, error) }-
PickerPeer用来找到key对应的远程节点,远程节点和PeerGetter一一对应,找到PeerGetter就找到对应的远程节点 -
PeerGetter根据group和key向远程节点获取数据
-
-
url.QueryEscape():net/url包,用于将url中的特殊字符用asll码进行替换,防止丢失 -
var _ PeerGetter = (*httpGetter)(nil):检验接口实现是否正确 -
day5需要实现http的客户端,对于缓存未命中时,需要向远程节点请求数据,此时被作为客户端,http.go中已经在day2实现了服务端的代码,需要再加上客户端-
HTTPPool实现了上面的PeerPicker接口,实现了其需要实现的PickPeer()函数,该函数返回远程节点对应的PeerGetter,找到PeerGetter就能去访问远程节点的数据go// 查找PeerGetter func (p *HTTPPool) PickPeer(key string) (PeerGetter, bool) { p.mu.Lock() defer p.mu.Unlock() if peer := p.peers.Get(key); peer != "" && peer != p.self { return p.httpGetters[peer], true } return nil, false } var _ PeerPicker = (*HTTPPool)(nil) // 检验接口实现是否正确 -
``httpGetter
实现了PeerGetter接口,实现了其需要实现的Get()函数,Get()函数主要是吊桶net/http中的Get()方法,向远程节点请求数据,并使用ioutil的ReadAll`获取gofunc (h *httpGetter) Get(group string, key string) ([]byte, error) { u := fmt.Sprintf( "%s%s%s", h.baseURL, url.QueryEscape(group), url.QueryEscape(key), ) res, err := http.Get(u) if err != nil { return nil, err } defer res.Body.Close() if res.StatusCode != http.StatusOK { return nil, fmt.Errorf("error response body: %v", err) } bytes, err := ioutil.ReadAll(res.Body) if err != nil { return nil, fmt.Errorf("error response body: %v", err) } return bytes, nil } var _ PeerGetter = (*httpGetter)(nil) // 检验接口实现是否正确 -
HTTPPool管理客户端的所有过程,因此需要加入httpGetter属性,其是一个map[string]*httpGetter类型,通过key映射httpGetter,同时加入day4中实现的一致性哈希过程,由于这些需要实现互斥访问哈希表,因此还需要一个mutexgotype HTTPPool struct { self string basePath string mu sync.Mutex peers *consistenthash.Map // 缓存值 httpGetters map[string]*httpGetter // 每个httpGetter对应一个远程节点,在缓存未命中时向远程节点请求数据 } -
最后,将
HTTPPool集成在主流程中,即group中,group需要实现RegisterPeer()函数,将对应的HTTPPool注入,同时封装getFromPeer()方法,其中调用了之前的Get()方法,用来请求远程节点的数据,重写一下load()方法,在不存在HTTPPool时才调用getLocally(),否则调用getFromPeergotype Group struct { name string // 缓存名称 getter Getter // 回调函数 mainCache cache // 缓存 peers PeerPicker // 集成HTTPPool } // 将HTTPPool注入到group中 func (g *Group) RegisterPeers(peer PeerPicker) { if g.peers != nil { panic("最多一个peerPicker") } g.peers = peer } // 缓存未命中 向远程节点请求 func (g *Group) load(key string) (Value ByteView, err error) { if g.peers != nil { if peer, ok := g.peers.PickPeer(key); ok { if value, err := g.getFromPeer(peer, key); err == nil { return value, nil } log.Println("获取Peer失败") } } // 不存在远程节点 调用回调函数 return g.getLocally(key) } // 向远处节点请求 func (g *Group) getFromPeer(peer PeerGetter, key string) (ByteView, error) { bytes, err := peer.Get(g.name, key) if err != nil { return ByteView{}, err } return ByteView{b: bytes}, nil } -
因此,流程如下
是 接收 key --> 检查是否被缓存 -----> 返回缓存值 ⑴ | 否 是 |-----> 是否应当从远程节点获取 -----> 与远程节点交互 --> 返回缓存值 ⑵ | 否 |-----> 调用`回调函数`,获取值并添加到缓存 --> 返回缓存值 ⑶
-
极客兔兔Gee-Cache Day5
益达爱喝芬达2024-10-09 0:03
相关推荐
Albert Edison14 小时前
【Redis】Centos7.9 安装 Redis 5 教程Steadfast_GG15 小时前
Redis中的通用命令tyung16 小时前
Go 手写 Wait-Free SPSC 无界队列:无 CAS、无锁、泛型节点池颜笑晏晏19 小时前
长输入短输出场景下的 SGLang 推理性能实测前缀缓存、PD 分离配比与参数调优真实的菜20 小时前
Redis 从入门到精通(十四):Redis 7.x 新特性全解 —— 系列收官之作小小工匠21 小时前
Redis - 缓存与数据库一致性:问题分析与解决方案闪电悠米21 小时前
黑马点评-Redis 消息队列-02_list_pubsub_limits折哥的程序人生 · 物流技术专研21 小时前
《Java 100 天进阶之路》第93篇:Redis实战应用:缓存策略与分布式锁(2026版)填满你的记忆1 天前
10万QPS下,Redis缓存如何避免雪崩?10WTW011 天前
QQ本地缓存机制初步探寻