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源码写的笔记,后续在学习过程中会不断更新。
posted @ 2020-12-21 17:45  月下繁星杨  阅读(128)  评论(0编辑  收藏  举报