网站建设与运营 市场分析,有哪些站内推广的方式,网站建设企业开发公司,注册网站域名要钱吗上一节我们实现了单机版的缓存服务#xff0c;但是我们的目标是分布式缓存。那么#xff0c;我们就需要把缓存服务部署到多态机器节点上#xff0c;对外提供访问接口。客户端就可以通过这些接口去实现缓存的增删改查。
分布式缓存需要实现节点间通信#xff0c;而通信方法…上一节我们实现了单机版的缓存服务但是我们的目标是分布式缓存。那么我们就需要把缓存服务部署到多态机器节点上对外提供访问接口。客户端就可以通过这些接口去实现缓存的增删改查。
分布式缓存需要实现节点间通信而通信方法常见的有HTTP和RPC。建立基于 HTTP 的通信机制是比较常见和简单的做法。
所以我们基于 Go 语言标准库 http 搭建 HTTP Server。
net/http标准库
简单实现一个http服务端例子。
type server intfunc (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {w.Write([]byte(Hello World!))
}func main() {var s serverhttp.ListenAndServe(localhost:9999, s)
}//下面的是http源码
type Handler interface {ServeHTTP(w ResponseWriter, r *Request)
}
在该代码中创建任意类型 server并实现 ServeHTTP 方法。
http.ListenAndServe 接收 2 个参数第一个参数是服务启动的地址第二个参数是 Handler实现了 ServeHTTP 方法的对象都可以作为 HTTP 的 Handler。
Cache HTTP 服务端
我们需要创建一个结构体去实现Handler接口而该结构体应该是有些属性变量来支撑我们做一些事情的。
HTTPPool 有 2 个参数一个是 addr用来记录自己的地址包括主机名/IP 和端口。另一个是 basePath作为节点间通讯地址的前缀默认是 /geecache/。比如http://example.com/geecache/开头的请求就用于节点间的访问。因为一个主机上还可能承载其他的服务加一段 Path 是一个好习惯。比如大部分网站的 API 接口一般以 /api 作为前缀。
HTTPPool实现了ServeHTTP方法即是Handler接口。
const defaultBasePath /geecache/type HTTPPool struct {addr string //本地IP端口 比如localhost:10000basePath string
}func (pool *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {//处理请求和响应后面会实现的
}//创建HTTPPool方法
func NewHTTPPool(addr string, basePath string) *HTTPPool {return HTTPPool{addr: addr,basePath: basePath,}
}
接下来实现最为核心的ServeHTTP方法。
func (pool *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {if !strings.HasPrefix(r.URL.Path, pool.basePath) {panic(HTTPPool serving unexpected path: r.URL.Path)}parts : strings.SplitN(r.URL.Path[len(pool.basePath):], /, 2)if len(parts) ! 2 {http.Error(w, bad request, http.StatusBadRequest)return}groupName : parts[0]group : GetGroup(groupName)if group nil {http.Error(w, no such group: groupName, http.StatusNotFound)return}view, err : group.Get(parts[1])if err ! nil {http.Error(w, err.Error(), http.StatusInternalServerError)return}w.Header().Set(Content-Type, application/octet-stream)w.Write(view.ByteSlice())
}
该方法是先判断前缀是否相等若不相等返回错误之后再判断分组名字再判断key。
url.Path的格式是/basepath/groupname/key。举个例子r.URL.Path是/geecache/scores/tom,那r.URL.Path[len(pool.basePath):]即是scores/tom接着就是使用strings.SplitN函数进行分割来获取分组名字和key。
测试
// 缓存中没有的话就从该db中查找
var db map[string]string{tom: 100,jack: 200,sam: 444,
}func main() {//传函数入参cache.NewGroup(scores, 210, cache.GetterFunc(funcCbGet))//传结构体入参也可以// cbGet : search{}// cache.NewGroup(scores, 210, cbGet)addr : localhost:10000peers : cache.NewHTTPPool(addr, cache.DefaultBasePath)log.Fatal(http.ListenAndServe(addr, peers))
}// 函数的
func funcCbGet(key string) ([]byte, error) {fmt.Println(callback search key: , key)if v, ok : db[key]; ok {return []byte(v), nil}return nil, fmt.Errorf(%s not exit, key)
}// 结构体实现了Getter接口的Get方法
type search struct {
}func (s *search) Get(key string) ([]byte, error) {fmt.Println(struct callback search key: , key)if v, ok : db[key]; ok {return []byte(v), nil}return nil, fmt.Errorf(%s not exit, key)
}
执行 go run main.go,查看效果 完整代码https://github.com/liwook/Go-projects/tree/main/go-cache/3-httpServer