-
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
相关推荐
信仰_27399324310 小时前
Mybatis一级缓存LB211220 小时前
Redis黑马点评 day01七夜zippoe1 天前
压缩与缓存调优实战指南:从0到1根治性能瓶颈(三)爬山算法1 天前
Redis(78) 如何设置Redis的缓存失效策略?程语有云1 天前
生产事故-Caffeine缓存误用之临下班的救赎shuair1 天前
redis大key问题悟能不能悟1 天前
缓存三剑客问题曹天骄1 天前
[特殊字符] 多环境 DCDN 缓存与 version 切换刷新方案七夜zippoe1 天前
压缩与缓存调优实战指南:从0到1根治性能瓶颈(二)快乐非自愿1 天前
Vue 缓存之坑,变量赋值方式和响应式数据