go语言路由流程初步解析.md
示例代码
package main
import (
"fmt"
"log"
"net/http"
"strings"
)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析参数,默认是不会解析的
fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello astaxie!") //这个写入到 w 的是输出到客户端的
}
func main() {
http.HandleFunc("/", sayhelloName) //设置访问的路由
err := http.ListenAndServe(":9090", nil) //设置监听的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
步骤说明
通过对 http 包的分析之后,现在让我们来梳理一下整个的代码执行过程。
首先调用 Http.HandleFunc
http.HandleFunc("/", sayhelloName) //设置访问的路由
按顺序做了几件事:
1. 调用了 DefaultServerMux 的 HandleFunc
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
//HandleFunc方法的实现,主要判断handle(sayhelloName)是否为空,然后把handle(sayhelloName)强转成HandlerFunc类型,这样就拥有了 ServHTTP 方法。
DefaultServeMux.HandleFunc(pattern, handler)
}
//
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
//官方翻译:HandlerFunc类型是一个适配器,允许将普通函数用作HTTP处理程序。如果f是具有适当签名的函数,HandlerFunc(f)是调用f的处理程序。type HandlerFunc func(ResponseWriter, *Request)
mux.Handle(pattern, HandlerFunc(handler))
}
//DefaultServeMux就是ServerMux;
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
//Server结构体源码
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry//路由规则,一个 string 对应一个 mux 实体,这里的 string 就是注册的路由表达式
es []muxEntry
hosts bool
}
//muxEntry的源码
type muxEntry struct {
h Handler//处理方法
pattern string//路由Api
}
//HandlerFunc的源码
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
2. 调用了 DefaultServerMux 的 Handle
//官方翻译:Handle注册给定模式的处理程序。如果模式的处理程序已经存在,请处理死机。
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")
}
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
}
//-----------------------------------
//如果路由规则和处理方法为空,重新创建并赋值
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
//把路由规则放入ServeMux中
e := muxEntry{h: handler, pattern: pattern}
mux.m[pattern] = e
if pattern[len(pattern)-1] == '/' {
mux.es = appendSorted(mux.es, e)
}
//-----------------------------------
if pattern[0] != '/' {
mux.hosts = true
}
}
3. 往 DefaultServeMux 的 map[string]muxEntry 中增加对应的 handler 和路由规则
其次调用 http.ListenAndServe(":9090", nil)
按顺序做了几件事情:
http.ListenAndServe(":9090", nil) //设置监听的端口
1. 实例化 Server
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
//Server对象源代码
type Server struct {
//Addr(可选)指定要侦听的服务器的TCP地址,
//在形式上”主机:端口". 如果为空,则使用“:http”(端口80)。
Addr string
//路由处理逻辑,如果为空默认使用http.DefaultServeMux,即使用自定义的路由逻辑(sayhelloName)
Handler Handler
//暂时不懂待更新
TLSConfig *tls.Config
//当前连接请求的超时时间
ReadTimeout time.Duration
//ReadHeaderTimeout是允许读取的时间量如果ReadHeaderTimeout为零,则使用ReadTimeout的值。如果两者都是零,没有超时。
ReadHeaderTimeout time.Duration
//WriteTimeout是超时前的可以最长持续时间返回响应
WriteTimeout time.Duration
IdleTimeout是等待
//等待下一个请求的时间,如果空闲超时为零,则使用ReadTimeout的值。如果两者都是零,没有超时。
IdleTimeout time.Duration
//MaxHeaderBytes控制服务器将读取解析请求头的键和值,包括请求行。它不限制请求正文的大小。如果为零,则使用DefaultMaxHeaderBytes。
MaxHeaderBytes int
//暂时不懂,待更新
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
//ConnState指定一个可选的回调函数当客户端连接更改状态时调用。见有关详细信息,请参阅ConnState类型和关联的常量。
ConnState func(net.Conn, ConnState)
//错误日志
ErrorLog *log.Logger
//此服务器传入请求的基本上下文。提供的侦听器是特定的侦听器即将开始接受请求。如果BaseContext为nil,则默认值为context.Background()如果非nil,则必须返回非nil上下文。
BaseContext func(net.Listener) context.Context
//不懂,待更新
ConnContext func(ctx context.Context, c net.Conn) context.Context
disableKeepAlives int32 // 以原子形式访问
inShutdown int32 // accessed atomically (non-zero means we're in Shutdown)以原子形式访问,如果为0默认关掉
nextProtoOnce sync.Once // guards setupHTTP2_* init
nextProtoErr error // result of http2.ConfigureServer if used
mu sync.Mutex
listeners map[*net.Listener]struct{}
activeConn map[*conn]struct{}
doneChan chan struct{}
onShutdown []func()
}
2. 调用 Server 的 ListenAndServe()
func (srv *Server) ListenAndServe() error {
if srv.shuttingDown() {
return ErrServerClosed
}
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(ln)
}
3. 调用 net.Listen("tcp", addr)监听端口
//在本地网络地址上收听广播。网络必须是“tcp”、“tcp4”、“tcp6”、“unix”或“unixpacket”。对于TCP网络,如果address参数中的主机为空或文本未指定的IP地址,则侦听将侦听本地系统的所有可用单播和选播IP地址。要只使用IPv4,请使用网络“tcp4”。地址可以使用主机名,但不建议这样做,因为它最多将为主机的一个IP地址创建侦听器。如果address参数中的端口为空或“0”,如“127.0.0.1:”或“[::1]:0”,则会自动选择端口号。侦听器的Addr方法可以用来发现所选端口。
func Listen(network, address string) (Listener, error) {
var lc ListenConfig
return lc.Listen(context.Background(), network, address)
}
4. 启动一个 for 循环,在循环体中 Accept 请求(Server.Serve()方法)
//处理接收客户端的请求信息
func (srv *Server) Serve(l net.Listener) error {
if fn := testHookServerServe; fn != nil {
fn(srv, l) // call hook with unwrapped listener
}
origListener := l
l = &onceCloseListener{Listener: l}
defer l.Close()
if err := srv.setupHTTP2_Serve(); err != nil {
return err
}
if !srv.trackListener(&l, true) {
return ErrServerClosed
}
defer srv.trackListener(&l, false)
baseCtx := context.Background()
if srv.BaseContext != nil {
baseCtx = srv.BaseContext(origListener)
if baseCtx == nil {
panic("BaseContext returned a nil context")
}
}
var tempDelay time.Duration // how long to sleep on accept failure
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, err := l.Accept()
if err != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := err.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay)
time.Sleep(tempDelay)
continue
}
return err
}
connCtx := ctx
if cc := srv.ConnContext; cc != nil {
connCtx = cc(connCtx, rw)
if connCtx == nil {
panic("ConnContext returned nil")
}
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(connCtx)
}
}
5. 对每个请求实例化一个 Conn,并且开启一个 goroutine 为这个请求进行服务 go c.serve()
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(connCtx)
6. 在serve方法中读取每个请求的内容 w, err := c.readRequest()
7. 判断 handler 是否为空,如果没有设置 handler(这个例子就没有设置handler),handler 就设置为 DefaultServeMux
//serve方法
serverHandler{c.server}.ServeHTTP(w, w.req)//调用业务逻辑(sayhelloName)
type serverHandler struct {
srv *Server
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}
8. 调用 handler 的 ServeHttp
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
9. 调用Hander方法根据 request 选择 handler,并且进入到这个 handler 的 ServeHTTPmux.handler(r).ServeHTTP(w, r)
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// CONNECT requests are not canonicalized.
if r.Method == "CONNECT" {
// If r.URL.Path is /tree and its handler is not registered,
// the /tree -> /tree/ redirect applies to CONNECT requests
// but the path canonicalization does not.
if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
return mux.handler(r.Host, r.URL.Path)
}
// All other requests have any port stripped and path cleaned
// before passing to mux.handler.
host := stripHostPort(r.Host)
path := cleanPath(r.URL.Path)
// If the given path is /tree and its handler is not registered,
// redirect for /tree/.
if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
if path != r.URL.Path {
_, pattern = mux.handler(host, path)
url := *r.URL
url.Path = path
return RedirectHandler(url.String(), StatusMovedPermanently), pattern
}
return mux.handler(host, r.URL.Path)
}
10. 选择 handler:
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
- 判断是否有路由能满足这个 request(循环遍历 ServerMux 的 muxEntry)
- 如果有路由满足,调用这个路由 handler 的 ServeHttp
- 如果没有路由满足,调用 NotFoundHandler 的 ServeHttp
自定义路由实现ServerHTTP接口
package main
import (
"fmt"
"net/http"
)
type MyMux struct {
}
func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request)
{
if r.URL.Path == "/" {
sayhelloName(w, r)
return
}
http.NotFound(w, r)
return
}
func sayhelloName(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello myroute!")
}
func main() {
mux := &MyMux{}
http.ListenAndServe(":9090", mux)
}
- 根据《Go Web编程》和go源码写的笔记,后续在学习过程中会不断更新。