Golang中net/http包源码分析与解释
2017-11-15 09:30
519 查看
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 4000 //一个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-net/http源码分析之http server
- Go源码分析 net/http包分析:追溯到socket
- golang net/http源码解读
- Golang中gzip过滤器的源码分析与解释
- 基于mjpg-streamer-r63的源码分析之:基础知识详细解释[二]
- 【Unity3D插件教程及源码分析】最好用的Http插件BestHttp 【教程上篇】
- Tornado源码分析之http服务器篇
- nginx http2 源码分析
- 基于mjpg-streamer-r63的源码分析之:基础知识详细解释[一]
- golang中net/http包用法
- golang.org/x/mobile/exp/gl/glutil/glimage.go 源码分析
- HTTP 头部解释,HTTP 头部详细分析,最全HTTP头部信息(1)
- Java源码分析系列之HttpServletRequest源码分析
- CAS进行https到http的改造方案,结合cookie源码分析
- 看Volley源码,对HTTP缓存机制分析
- Asp.net web Api源码分析-HttpResponseMessage
- HTTP头部信息解释分析(详细整理)
- Tornado源码分析之http服务器篇
- 基于mjpg-streamer-r63的源码分析之:基础知识详细解释[二]
- Android 4.4以上使用HttpURLConnection底层使用OkHttp实现的源码分析