go net/http 底层库实现原理

1,015 阅读4分钟

📖 Net/http 底层实现原理.

了解到能够使用net/http对外进行简单进行扩展,更深层次的理解以后进行分析。

📜 一、net/http库的简单使用.

func main() {
	
	http.HandleFunc("/test", func(response http.ResponseWriter, request *http.Request) {
		// 请求.
		str := "Hello Word"
		fmt.Println(str)
		response.Write([]byte(str))
	})
	// 不传handler,则使用默认的handler, 而上面直接通过包名来进行诸如HandleFunc底层则是会注入到DefaultServeMux中.
	http.ListenAndServe(":8080", nil)
}

结果:可以看到其比Java中开启一个Web应用是多么便捷,但这不是本文的重点.

image-20211125220304937

📜 二、底层实现原理分析.

我们仍然从上图代码块分析,首先先分析http.HandlerFunc注入Handler来具体分析.

先说下最终结果就是,其注入的Handler最后都会注入到下面的结构体中.

type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry  // entry 具体存放具体请求的Handler,由请求路径和具体的Handler构成.
	es    []muxEntry // slice of entries sorted from longest to shortest.
	hosts bool       // whether any patterns contain hostnames
}

虽然说最终会存入改结构体中,但是我们要知道其实如何存入的呢? 存入之后又是怎么进行调用的,所以,接着往下看.

当我们点进去我们写得代码之后,会发现,其会将我们自己的Handler转换为HandlerFunc,而改结构体实现了net\http下的Handler接口,并重写了ServerHTTP方法,在该方法内部直接调用我们自己的handler.

// 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")
	}
	// 将func表示的handler转换为net/http中的HandlerFunc,并在其接口ServerHTTP中调用handler.
	mux.Handle(pattern, HandlerFunc(handler))
}

// HandlerFunc定义.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

接下来就是分析ServerMux是如何进行保存Handler的.

可以看到会将Handler注册到ServeMux中的m和es中.

// 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 mux.m == nil {
		mux.m = make(map[string]muxEntry)
	}
	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
	}
}

那么到了这里,我们就会发现,我们所有的的Handler最终都会转换成net/http中的Handler,并且保存在ServerMux中的Map中.

**现在我们来看看其具体是如何进行分发请求的:**其最终会执行到如何下代码: 在这里我将一些基本简单的代码将会进行删除掉. 注释也是很重要的.

func (srv *Server) Serve(l net.Listener) error {

	defer srv.trackListener(&l, false)

	var tempDelay time.Duration // how long to sleep on accept failure
	
	ctx := context.WithValue(baseCtx, ServerContextKey, srv)
	for {
        // 重点:通过for循环来接受请求.
		rw, err := l.Accept()
		connCtx := ctx
		tempDelay = 0
		// 构造一个新的conn,然后开启goroutine去执行本次请求.
		c := srv.newConn(rw)
		c.setState(c.rwc, StateNew, runHooks) // before Serve can return
		go c.serve(connCtx)
	}
}

其最终会执行到

// TODO 很重要的方法.
serverHandler{c.server}.ServeHTTP(w, w.req)

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
	handler := sh.srv.Handler
	// 如果当前的Server中的Handler为空,那么就使用http库中默认的Handler.
	// 我们可以将这一部分替换成我们自己实现的代码.
	if handler == nil {
        // 使用默认.
        // 我们可以自己构造这个. 然后就是当代码执行到这这里之后将会根据我们自己的意图来进行实现.
		handler = DefaultServeMux
	}
    
	if req.RequestURI == "*" && req.Method == "OPTIONS" {
		handler = globalOptionsHandler{}
	}

	// handler--->ServerMux.
  	// 我们来看看该方法的具体实现.
	handler.ServeHTTP(rw, req)
}

// 就是根据request然后去ServeMux中找到属于当前请求的Handler.
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)
}

下面代码就是根据Request来从ServeMux中到属于当前请求的Handler.

如果我们要自己开发框架的话,我们可以复用net/http解析请求的流程,直接使用期解析请求好之后的流程,就好比复用Java中的Servlet一样,我当初自己实现过自己解析HTTP请求,最终只实现了Get和POST请求,但是图片并没有实现,最终解析图片失败. 所以我要自己实现Web框架的话会复用net/http底层库.

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)
		u := &url.URL{Path: path, RawQuery: r.URL.RawQuery}
		return RedirectHandler(u.String(), StatusMovedPermanently), pattern
	}

    // 直接从map中取出自己的对应的handler.
	return mux.handler(host, r.URL.Path)
}

📜 三、总结.

net/http其实就是将我们自己的HandleFunc注册到默认的ServeMux也就是DefaultServeMux中,监听端口时会构建一个Server,而该Server内的Handler将会时为Nil, 当请求解析完毕之后会直接调用serverHandler{c.server}.ServeHTTP(w, w.req)再该方法内部,如果Server的Handler为nil,那么就会使用默认的DefaultServerMux作为当前Server的Handler,然后进行ServerHTTP,如果我们进行实现我们自己的框架,我们可以通过实现一个自己的Handler,并且在创建Server的时候通过给Server赋值Handler从而达到实现我们自己的目的. 欢迎关注公众号:小马正在写Bug.