Golang中net/http包源码分析与解释

Golang中net/http包源码分析与解释

关于Golang语言的源码,我一直是使用LiteIDE中自带的源码阅读功能来解读的。这次对于net/http包的源码阅读与分析也是通过LiteIDE软件来完成的。
打开net/http包的相关源码,可以看出Golang对于http相关的操作有很多很完善的源码支持。比如对于server、client、transfer等等的操作。

由于源码数量和功能很多,所以下面就以一个简单的web服务器来简单解析一下server中的相关函数和操作对应的源码。

一个简单的Web Server

package main

import (
    "fmt"
    "net/http"
    "log"
)

func main() {
    http.HandleFunc("/", sayhelloName)       //设置访问的路由
    err := http.ListenAndServe(":9090", nil) //设置监听的端口
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

对于它的实现,我们可以看到它使用了net/http包中的HandleFunc()函数以及ListenAndServe()函数,下面我们来分别解析一下这两个函数。

http.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)
}

可看出该函数调用了DefaultServeMux中的函数,接下来看看DefaultServeMux.HandleFunc函数的实现:

// NewServeMux allocates and returns a new ServeMux.
var DefaultServeMux = NewServeMux()
func NewServeMux() *ServeMux { return &ServeMux{m: make(map[string]muxEntry)} }

type ServeMux struct {
    mu    sync.RWMutex                   //一个读写锁
    m     map[string]muxEntry            //一个path(patterns)的映射map
    hosts bool                          // whether any patterns contain hostnames
}

再追溯到mux与ServeMux:

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
    if r.Method != "CONNECT" {
        if p := cleanPath(r.URL.Path); p != r.URL.Path {
            _, pattern = mux.handler(r.Host, p)
            url := *r.URL
            url.Path = p
            return RedirectHandler(url.String(), StatusMovedPermanently), pattern
        }
    }
    return mux.handler(r.Host, r.URL.Path)
}

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
}

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)
}

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}
func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()

    if pattern == "" {
        panic("http: invalid pattern " + pattern)
    }
    if handler == nil {
        panic("http: nil handler")
    }
    if mux.m[pattern].explicit {
        panic("http: multiple registrations for " + pattern)
    }

    mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}

    if pattern[0] != '/' {
        mux.hosts = true
    }

    // Helpful behavior:
    // If pattern is /tree/, insert an implicit permanent redirect for /tree.
    // It can be overridden by an explicit registration.
    n := len(pattern)
    if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
        // If pattern contains a host name, strip it and use remaining
        // path for redirect.
        path := pattern
        if pattern[0] != '/' {
            // In pattern, at least the last character is a '/', so
            // strings.Index can't be -1.
            path = pattern[strings.Index(pattern, "/"):]
        }
        mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern}
    }
}

这样就完成了HandleFunc的实现和执行。
可见通过添加路由,Handler处理的入口就是serverHandler{c.server}.ServeHTTP(w, w.req),最终到HandleFunc的执行。

http.ListenAndServe函数源代码解析

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

可看出该函数调用了server中的函数,接下来看看server.ListenAndServe函数的实现:

func (srv *Server) ListenAndServe() error {
    addr := srv.Addr
    if addr == "" {
        addr = ":http"
    }
    ln, err := net.Listen("tcp", addr)
    if err != nil {
        return err
    }
    return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

可见,该函数中又调用到了srv.Serve函数,接下来我们追溯到这个函数来看看:

// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each.  The service goroutines read requests and
// then call srv.Handler to reply to them.
func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    var tempDelay time.Duration // how long to sleep on accept failure
    for {
        rw, e := l.Accept()
        if e != nil {
            if ne, ok := e.(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", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        tempDelay = 0
        c, err := srv.newConn(rw)
        if err != nil {
            continue
        }
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve()
    }
}

可见,server为每一个请求建立一个连接,同时进行逻辑的处理。这样就完成了ListenAndServe的实现和执行。

总结

http.HandleFunc:
调用http.HandleFunc->调用DefaultServerMux.HandleFunc->调用DefaultServerMux的Handle

http.ListenAndServe:
实例化Server->调用Server.ListenAndServe()->为每一个请求建立一个连接,同时进行逻辑的处理,进行go c.serve(),读取每个请求的内容->调用handler.ServeHttp->根据request选择handler->mux.handler(r).ServeHTTP(w,r),选择handler。

根据对源码的分析,对整个函数的执行过程和方法有了更深刻的理解,不由得说,分析源码是学习一门语言最好的方式。对于net/http包的学习让我对 http 协议的工作原理和实现技术有了初步了解。

你可能感兴趣的:(golang)