原文地址,转载请注明出处: https://blog.csdn.net/qq_34021712/article/details/109907490 ©王赛超
目录
go 版本
go version go1.15.1 darwin/amd64
一. 搭建简单的web服务器
对于Go,实现一个最简单的http server用不着几行代码,如下:
func main() {
http.HandleFunc("/", HelloServer)
_ = http.ListenAndServe(":8080", nil)
}
func HelloServer(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(`hello world`))
if err != nil {
fmt.Println(err)
}
}
以上代码可以简单的分为3部分:
请求处理函数
func HelloServer(w http.ResponseWriter, r *http.Request)
注册路由
http.HandleFunc("/", HelloServer)
监听端口启动服务
_ = http.ListenAndServe(":8080", nil)
在浏览器输入http://localhost:8080得到输出 hello world。
请求处理函数就是真正处理业务逻辑的。这块没什么可说的,主要讲一下 http.HandleFunc 和 http.ListenAndServe。这块的内容打算分为2章来讲,主要是内容有点多。
二.首先,先分析一下http.HandleFunc()这个函数
http.HandleFunc()
直接进入函数HandleFunc的声明,源码如下:
// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
// 注册路由
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
这个函数就是注册路由,HandleFunc的第一个参数是路由表达式,也就是请求路径,第二个参数是一个函数类型,也就是真正处理请求的函数。没有其他逻辑,直接调用DefaultServeMux.HandleFunc()处理。
DefaultServeMux.HandleFunc()
相关源码如下:
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {
h Handler
pattern string
}
// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return new(ServeMux) }
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
// 定义好结构体,但是并没有显式初始化时,默认情况下会为结构体的字段分配其类型的零值
var defaultServeMux ServeMux
可以看到,DefaultServeMux是一个默认的ServeMux。
ServeMux的结构体中:
mu sync.RWMutex:是一个并发控制的锁。
m map[string]muxEntry: 是一个key为string ,value为muxEntry结构的map,用于路由的精确匹配。
es []muxEntry: 是 muxEntry 切片。存储 "/" 结尾的,用于路由的部分匹配。 例如在浏览器输入请求路径 "/test/aaa" 和 "/test/bbb" 都会被路由到 "/test/" 这个路由的处理函数上。
hosts bool: 标记路由中是否带有主机名。
muxEntry结构体中:
pattern string: 路由表达式,请求路径。
h Handler: 对应的请求处理函数。
Handler定义如下:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
Handler是一个接口!!只有一个方法ServeHTTP(ResponseWriter, *Request)。也就是说任何结构只要实现了这个ServeHTTP方法,那么这个结构体就是一个Handler对象。
go的http服务都是基于Handler进行处理,而Handler对象的ServeHTTP方法会读取Request进行逻辑处理然后向ResponseWriter中写入响应,这个到下一章讲http.ListenAndServe的时候再细讲。
回到上面的http.HandleFunc()函数,它调用了DefaultServeMux.HandleFunc()将处理器注册到指定路由规则上。
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
主要代码为 mux.Handle(pattern, HandlerFunc(handler)) 这里的 HandlerFunc 是将handler函数做类型转换,转换为http.HandlerFunc类型,main方法中注册路由时调用的是 http.HandleFunc(),这里类型是http.HandlerFunc。多了一个r字母,为什么要做转换?看一下HandlerFunc的定义:
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
HandlerFunc类型是一个适配器,并且这种类型实现了ServeHTTP方法,并在ServeHTTP方法中又调用了被转换的函数自身,也就是说这个类型的函数其实就是一个Handler类型的对象,通过类型转换可以将一个具有func(ResponseWriter, *Request)签名的普通函数转换为一个Handler对象,而不需要再定义一个结构体,再让这个结构实现ServeHTTP方法,非常方便的将普通函数用作HTTP处理程序。
mux.Handle(pattern, HandlerFunc(handler))
理解完HandlerFunc(handler),再来看看整句mux.Handle(pattern, HandlerFunc(handler))
// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
// 检查路由表达式是否为空
if pattern == "" {
panic("http: invalid pattern")
}
// 检查处理函数是否为空
if handler == nil {
panic("http: nil handler")
}
// 如果这个路由表达式已经注册过处理器函数,直接panic
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
}
// 如果 mux.m 为nil 通过 make 函数初始化 map
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
// 用路由表达式和处理函数handler创建 muxEntry 对象
e := muxEntry{h: handler, pattern: pattern}
// 将创建的 muxEntry 对象 添加进 map中 key为路由表达式pattern value为muxEntry对象
mux.m[pattern] = e
if pattern[len(pattern)-1] == '/' {
// 如果路由patterm以'/'结尾,则将对应的muxEntry对象加入到[]muxEntry中,路由长的位于切片的前面
mux.es = appendSorted(mux.es, e)
}
// 如果该路由路径不以 "/" 开始,标记该 mux 中有路由的路径带有主机名
if pattern[0] != '/' {
mux.hosts = true
}
}
至此,第一个关键的函数http.HandleFunc("/", HelloServer)就分析完了。就是把传进来的pattern和handler保存在muxEntry结构中,并且pattern作为key,把muxEntry装入到DefaultServeMux的Map里面。简单来说就是保存当前路由和自己定义的那个处理函数。