本文是晚辈对net/http包的一点浅显的理解,文中若有错误的地方请前辈们指出,以避免误导!html
转摘本文也请注明出处:Go语言备忘录(3):net/http包的使用模式和源码解析,多谢! 缓存
目录:
安全
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
type HandlerFunc func(ResponseWriter, *Request) //实现Handler接口的ServeHTTP方法 func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) //调用自身 }
var addr = flag.String("addr", ":8080", "http server address") //1.不带参数处理函数 func serveHome(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } http.ServeFile(w, r, "home.html") } //2.带参数处理函数,闭包函数隐式转换为http.HandlerFunc函数类型 func myHandler(s string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } http.ServeFile(w, r, s) //使用参数s } } func main() { flag.Parse() //向默认路由注册处理器函数 http.HandleFunc("/", serveHome) //或http.Handle("/", http.HandlerFunc(serveHome)) http.Handle("/file",myHandler("somefile")) err := http.ListenAndServe(*addr, nil) //启动监听,第二个参数nil表示使用默认路由DefaultServeMux中注册的处理器 if err != nil { log.Fatalln("ListenAndServe: ", err) } }
func main() { mux := http.NewServeMux() //新建一个自定义的路由 mux.Handle("/file",myHandler("somefile")) mux.HandleFunc("/", serveHome) err := http.ListenAndServe(*addr,mux) //启动监听 if err != nil { log.Fatalln("ListenAndServe: ", err) } }
mux := http.NewServeMux() mux.Handle("/file",myHandler("somefile")) mux.HandleFunc("/", serveHome) s := &http.Server{ Addr: ":8080", Handler: mux, //指定路由或处理器,不指定时为nil,表示使用默认的路由DefaultServeMux ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, ConnState: //指定链接conn的状态改变时的处理函数 //.... } log.Fatal(s.ListenAndServe())
接下来,咱们就跟踪源码来仔细的分析下整个执行过程。服务器
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
2.而server.ListenAndServe()方法内部调用net.Listen("tcp", addr),该方法内部又调用net.ListenTCP()建立并返回一个监听器net.Listener,以下的ln;闭包
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)}) }
type tcpKeepAliveListener struct { *net.TCPListener } func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { tc, err := ln.AcceptTCP() if err != nil { return } tc.SetKeepAlive(true) //发送心跳 tc.SetKeepAlivePeriod(3 * time.Minute) //发送周期 return tc, nil }
func (srv *Server) Serve(l net.Listener) error { defer l.Close() if fn := testHookServerServe; fn != nil { fn(srv, l) } var tempDelay time.Duration //重试间隔 if err := srv.setupHTTP2_Serve(); err != nil { return err } srv.trackListener(l, true) //缓存该监听器 defer srv.trackListener(l, false) //从缓存中删除当前监听器 baseCtx := context.Background() ctx := context.WithValue(baseCtx, ServerContextKey, srv) //新建一个context用来管理每一个链接conn的Go程 for { rw, e := l.Accept() //调用tcpKeepAliveListener对象的 Accept() 方法 if e != nil { select { case <-srv.getDoneChan(): return ErrServerClosed //退出Serve方法,并执行延迟调用(从缓存中删除当前监听器) default: } //若是发生了net.Error错误,则隔一段时间就重试一次,间隔时间每次翻倍,最大为1秒 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 := srv.newConn(rw) //该方法根据net.Conn、srv构造了一个新的http.conn类型 c.setState(c.rwc, StateNew) //缓存该链接的状态,若是方法:Server.ConnState(net.Conn, ConnState)不为nil,就根据当前链接的状态执行它 go c.serve(ctx) } }
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) }
6.如上源码能够看到,当 handler == nil 时使用默认的DefaultServeMux路由,不然使用在第1步中为Serve指定了的Handler;而后调用该Handler的ServeHTTP方法(该Handler通常被设置为路由ServeMux类型);tcp
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) //规范化请求的路径格式,查找最匹配的Handler h.ServeHTTP(w, r) }
type HandlerFunc func(ResponseWriter, *Request) //实现Handler接口的ServeHTTP方法 func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) //调用自身 }
func main() { mux := http.NewServeMux() mux.Handle("/to",http.RedirectHandler("http://example.org", 307)) err := http.ListenAndServe(*addr,mux) //启动监听 if err != nil { log.Fatalln("ListenAndServe: ", err) } }
Get、Head、Post和PostForm函数发出HTTP/ HTTPS请求。函数
resp, err := http.Get("http://example.com/") ... resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf) ... resp, err := http.PostForm("http://example.com/form", url.Values{"key": {"Value"}, "id": {"123"}})
程序在使用完回复后必须关闭回复的主体。url
resp, err := http.Get("http://example.com/") if err != nil { // handle error } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) // ...
要管理HTTP客户端的头域、重定向策略和其余设置,建立一个Client:spa
client := &http.Client{ CheckRedirect: redirectPolicyFunc, } resp, err := client.Get("http://example.com") // ... req, err := http.NewRequest("GET", "http://example.com", nil) // ... req.Header.Add("If-None-Match", `W/"wyzzy"`) resp, err := client.Do(req) // ...
要管理代理、TLS配置、keep-alive、压缩和其余设置,建立一个Transport:操作系统
tr := &http.Transport{ TLSClientConfig: &tls.Config{RootCAs: pool}, DisableCompression: true, } client := &http.Client{Transport: tr} resp, err := client.Get("https://example.com")
Client和Transport类型均可以安全的被多个go程同时使用。出于效率考虑,应该一次创建、尽可能重用。