Dawn's Blogs

分享技术 记录成长

0%

从零实现分布式缓存 (3) HTTP服务端

本节实现了分布式缓存 HTTP 服务端的搭建,最终代码结构如下:

1
2
3
4
5
6
7
8
9
lru/
|--lru.go
|--lru_test.go
byteview.go
cache.go
dawncache.go
dawncache_test.go
go.mod
http.go

HTTP 服务端

http.go

HTTPPool

构建结构体 HTTPPool 用于作为服务端,用于响应查询缓存数据的请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
const defaultBasePath = "/_dawncache/"

type HTTPPool struct {
self string // 如 http://127.0.0.1:8080
basePath string // 节点间通讯地址的前缀,如 http:// 127.0.1:8080/basePath/groupName/key 用于请求数据
}

func NewHTTPPool(self string) *HTTPPool {
return &HTTPPool{
self: self,
basePath: defaultBasePath,
}
}

实现 http.Handler 接口

当 HTTPPool 实现了 http.Handler 接口时,可以传入 http.ListenAndServe 函数作为第二个参数。

而 http.Handler 的接口定义如下:

1
2
3
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}

所以 HTTPPool 需要实现 ServeHTTP 方法,以此实现 http.Handler 接口。ServeHTTP 中的主要逻辑就是从 URL.Path 中提取出 groupName 和 key,并在相应的 group 中查询缓存数据并响应 HTTP 请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// Log 输出日志信息
func (p *HTTPPool) Log(format string, v ...interface{}) {
log.Printf("[server %s] %s", p.self, fmt.Sprintf(format, v...))
}

// ServeHTTP 处理查询缓存的请求,实现了 http.Handler 接口
func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 判断是否有 basePath
if !strings.HasPrefix(r.URL.Path, p.basePath) {
http.Error(w, "HTTPPool serving unexpected path: "+r.URL.Path, http.StatusBadRequest)
return
}

// 检查是否有 groupName 和 key
parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)
if len(parts) != 2 {
http.Error(w, "bad request", http.StatusBadRequest)
return
}

groupName := parts[0]
key := parts[1]

// 通过 groupName 获取 group
group := GetGroup(groupName)
if group == nil {
http.Error(w, "no such group:"+groupName, http.StatusBadRequest)
return
}

// 从缓存中获取数据
view, err := group.Get(key)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

// 响应客户端
w.Header().Set("Content-Type", "application/octet-stream")
w.Write(view.ByteSlice())
}