计算机网站建设及管理,wordpress 内外网,网站建设的成果怎么写,企业如何制作网站管理系统经过了前两个章节的学习#xff0c;分布式缓存的存储与新增我们已经实现了#xff0c;并且对其做了高可用处理。本章节我们剥离和缓存强相关的逻辑#xff0c;开始搭建一个HTTP服务器#xff0c;毕竟缓存数据库搭建完之后别人没法访问也是没有用处的。这一章节我们重点学习… 经过了前两个章节的学习分布式缓存的存储与新增我们已经实现了并且对其做了高可用处理。本章节我们剥离和缓存强相关的逻辑开始搭建一个HTTP服务器毕竟缓存数据库搭建完之后别人没法访问也是没有用处的。这一章节我们重点学习go原生的HTTP标准库。 前文链接 手撕分布式缓存之一 | 定义缓存结构体与实现底层功能函数 手撕分布式缓存之二 | 互斥锁的优化 系列目录 1HTTP Server 的搭建1.1前言1.2HTTP 核心数据结构的构造及初始化函数的实现1.3单节点HTTP Server核心Get方法的实现1.4代码实现及解释 1HTTP Server 的搭建
1.1前言 通过本篇文章可以学习到 go 语言原生的 HTTP Server 的使用虽然不像成熟的Web框架那样方法、机制都很完全但是通过学习底层的功能实现对于我们整体项目结构搭建是非常重要的。如果将go语言、go衍生技术ginMiddleWare等等一系列相关的技术看做一整个项目结构那么哪些技术我们要实现哪些功能呢这些功能做出来实际上使用的人有多少呢能给我们带来多少价值这些问题具象来看的话是与我们开发人员息息相关的因此我们有必要去学习这种看似吃力不讨好实际却会给我们带来很深远的收益的知识。
1.2HTTP 核心数据结构的构造及初始化函数的实现 go语言的HTTP服务为我们提供了 ListenAndServe(addr string, handler Handler) error 这个方法handler是HTTP服务的主体处理函数任何实现了 ServeHTTP 方法的对象都可以作为handler的值传递给ListenAndServe 方法。该方法除了handler参数还需要接收一个addr用来标识服务启动的地址和端口这个也是服务启动之后其他请求者访问的域名。因此我们可以定义一个数据结构HTTPPool里面有属性 self string 和 basePath stringself是就是上面说的addrbasePath是用于区分服务器上其他服务的请求Url避免重复比如我们可以默认 /_geecache/ 作为我们请求URI统一资源标识符第一级的值。
1.3单节点HTTP Server核心Get方法的实现 在1.2小节我们解释了HTTP的核心数据结构这一小节我们讨论下HTTP服务的核心服务ServeHTTP。在前言中我们概括性的讨论了go的HTTP原生包下称HTTP原生包与成熟框架是有区别的从相关的代码中gin和geeCache的相关实现中了解到HTTP原生包对于方法的直接操作只有ServeHTTP方法也就是说如果只使用HTTP原生包构建HTTP Server所有的请求都是由 ServeHTTP 方法首先处理的而例如Gin框架可以将方法绑定在URI上在路由注册之后可以通过URI访问与其绑定的功能方法从HTTP原生包来看我能想到的实现Gin这一功能的方法是在ServeHTTP方法中通过判断请求的URI的值与if语句过滤来实现不同的URI访问不同的功能函数。 HTTP Get方法的实现并不复杂主要流程还是调用下层我们封装的Get方法如何封装的可以阅读 分布式系列 的前两章内容。
1.4代码实现及解释
自定义默认的URI第一级的值 和 定义HTTP的核心数据结构 自定义URI的第一级的值为 _geecache 使用下划线开头的值用作区分重复的可能性更低因此我们使用下划线开头。 // this is the defaultPath, user it to distinguish geeCache Project and other Project.
const defaultBasePath /_geecache/type HTTPPool struct {// this peers base URL, e.g. self is https://example.net:8000self stringbasePath string
}// NewHTTPPool initializes an HTTP pool of peers.
func NewHTTPPool(self string) *HTTPPool {return HTTPPool{self: self,basePath: defaultBasePath,}
}核心功能Get函数的实现 在通过URI获取GroupName之前首先判断URI的第一级值是否与defaultPath一致通过strings.SplitN根据 **字符 \ ** 进行截取并获得groupName 和 key调用下层已经实现的函数功能以从中获取key对应的value使用 w.Write(view.ByteSlice())返回给 请求者一个 copy的对象避免返回给请求者原对象后遭到恶意修改吗view.ByteSlice() 的具体实现在上一章节有具体说明和实现 。 // ServeHTTP handle all http requests
func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {// 1. judge the first layer of uri is defaultBasePath.if !strings.HasPrefix(r.URL.Path, p.basePath) {panic(HTTPPool serving unexpected path: r.URL.Path)}// begin from the defaultPath, then start to split the rest of uri and get groupName from this operationparts : strings.SplitN(r.URL.Path[len(p.basePath):], /, 2)// 2. judge the structure have two layers, first is groupName, second is key// the structure is /basepath/groupname/keyif len(parts) ! 2 {http.Error(w, bad request, http.StatusBadRequest)return}// 3. get GroupName and keygroupName : parts[0]key : parts[1]// 4. call Underlying interface to get Groupgroup : GetGroup(groupName)if group nil {http.Error(w, no such group: groupName, http.StatusNotFound)return}// 5. call Underlying interface to get key-valueview, err : group.Get(key)if err ! nil {http.Error(w, err.Error(), http.StatusInternalServerError)return}w.Header().Set(Content-Type, application/octet-stream)// return key-valuew.Write(view.ByteSlice())
}HTTP Server 服务的启动(main函数的编写)参数的声明 db 用于验证GetterFunc方法是否生效。210是二进制左移10位对应的十进制为4096。http.ListenAndServe 是服务的启动代码。 // not necessary, just is a test demo in here
var db map[string]string{Tom: 630,Jack: 589,Sam: 567,
}func main() {// 210 means Binary Left Shift 10 bits, its value is 2048// GetterFunc can ignore the datas addition, cause it initialize the group scores, and state the maxBytesgeecache.NewGroup(scores, 210, geecache.GetterFunc(func(key string) ([]byte, error) {log.Println([SlowDB] search key, key)if v, ok : db[key]; ok {return []byte(v), nil}return nil, fmt.Errorf(%s not exist, key)}))addr : localhost:9999peers : geecache.NewHTTPPool(addr)log.Println(geecache is running at, addr)// log the ListenAndServes errorlog.Fatal(http.ListenAndServe(addr, peers))
}