flowchart TD
Start([接收 key]) --> CheckCache{检查是否被缓存}
CheckCache -->|是| ReturnCache1[返回缓存值 ⑴]
CheckCache -->|否| CheckRemote{是否应当从远程节点获取}
CheckRemote -->|是| HashSelect[使用一致性哈希选择节点]
HashSelect --> IsRemote{是否是远程节点}
IsRemote -->|是| HTTPClient[HTTP 客户端访问远程节点]
HTTPClient --> Success{成功?}
Success -->|是| ReturnCache2[返回缓存值 ⑵]
Success -->|否| FallbackLocal[回退到本地节点处理]
IsRemote -->|否| FallbackLocal
CheckRemote -->|否| Callback[调用回调函数]
Callback --> AddCache[添加到缓存]
AddCache --> ReturnCache3[返回缓存值 ⑶]
style Start fill:#f9f,stroke:#333,stroke-width:2px
style ReturnCache1,ReturnCache2,ReturnCache3 fill:#9f9,stroke:#333,stroke-width:2px
抽象 PeerPicker
go
type PeerPicker interface{
PickPeer(key string) (peer PeerGetter, ok bool)
}
type PeerGetter interface{
Get(group string,key string) ([]byte,error)
}
-
在这里,抽象出 2 个接口,PeerPicker 的
PickPeer()
方法用于根据传入的 key 选择相应节点 PeerGetter。 -
接口 PeerGetter 的
Get()
方法用于从对应 group 查找缓存值。PeerGetter 就对应于上述流程中的 HTTP 客户端。
节点选择与HTTP客户端
go
// httpGetter 是一个结构体,包含 baseURL 字段,
// 用作所有 HTTP GET 请求的基础 URL。
// Get 是 httpGetter 的一个方法,用于通过特定的 URL 来获取数据,
// URL 由 baseURL、group 和 key 参数组合生成。
func (h *httpGetter) Get(group string, key string) ([]byte, error) {
// 构造完整的 URL,将 baseURL 与编码后的 group 和 key 参数拼接起来。
// 发送 HTTP GET 请求到构造的 URL。
if err != nil {
// 如果请求出现错误,则返回错误信息。
}
// 确保函数退出时关闭响应体。
// 检查服务器响应状态是否为 OK(200),如果不是则返回错误。
if res.StatusCode != http.StatusOK {
}
// 读取整个响应体。
if err != nil {
// 如果读取响应体时出错,则返回错误信息。
}
// 返回读取到的字节和 nil 错误,表示成功。
}
// 这行代码确保 httpGetter 实现了 PeerGetter 接口。
// 如果 httpGetter 没有满足 PeerGetter 接口要求,将会引发编译错误。
- baseURL 表示将要访问的远程节点的地址,例如
http://example.com/_geecache/
。 - 使用
http.Get()
方式获取返回值,并转换为[]bytes
类型
第二步,为HTTPPool添加节点选择的功能
go
const (
defaultBasePath = "/_geecache/"
defaultReplicas = 50
)
// HTTPPool implements PeerPicker for a pool of HTTP peers.
type HTTPPool struct {
// this peer's base URL, e.g. "https://example.net:8000"
self string
basePath string
mu sync.Mutex // guards peers and httpGetters
peers *consistenthash.Map
httpGetters map[string]*httpGetter // keyed by e.g. "http://10.0.0.2:8008"
}
- 新增成员变量
peers
,类型是一致性哈希算法的Map
,用来根据具体的 key 选择节点。 - 新增成员变量
httpGetters
,映射远程节点与对应的 httpGetter。每一个远程节点对应一个 httpGetter,因为 httpGetter 与远程节点的地址baseURL
有关
第三步,实现PeerPicker接口
go
func (p *HTTPPool) Set(peers ...string){
p.mu.Lock()
defer p.mu.Unlock()
p.peers = consistenthash.New(defaultReplicas,nil)
p.peers.Add(peers...)
p.httpGetters = make(map[string]*httpGetter, len(peers))
for _,peer := range peers{
p.httpGetters[peer] = &httpGetter{baseURL: peer + p.basePath}
}
}
Set()
方法实例化了一致性哈希算法,并且添加了传入的节点。- 并为每一个节点创建了一个 HTTP 客户端
httpGetter
。 PickerPeer()
包装了一致性哈希算法的Get()
方法,根据具体的 key,选择节点,返回节点对应的 HTTP 客户端。
至此,HTTPPool 既具备了提供 HTTP 服务的能力,也具备了根据具体的 key,创建 HTTP 客户端从远程节点获取缓存值的能力。