当前位置: 首页 > 工具软件 > gonet2 > 使用案例 >

Go使用net/http标准库(一)源码学习之- http.HandleFunc()

太叔俊侠
2023-12-01

原文地址,转载请注明出处: 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.HandleFunchttp.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: 是一个keystringvaluemuxEntry结构的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对象。

gohttp服务都是基于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)就分析完了。就是把传进来的patternhandler保存在muxEntry结构中,并且pattern作为key,把muxEntry装入到DefaultServeMuxMap里面。简单来说就是保存当前路由和自己定义的那个处理函数

下一章分析 http.ListenAndServe(":8080", nil)。

 类似资料: