原文地址,转载请注明出处: https://blog.csdn.net/qq_34021712/article/details/109907490 ©王赛超
go version go1.15.1 darwin/amd64
对于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章来讲,主要是内容有点多。
直接进入函数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()
处理。
相关源码如下:
// 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
处理程序。
理解完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
里面。简单来说就是保存当前路由
和自己定义的那个处理函数
。