net/http
web 示例:
package mainimport ( "fmt" "net/http")func main() { http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello World") }) http.ListenAndServe("localhost:8080", nil)}
- 进行逻辑解决
func(w http.ResponseWriter, r *http.Request){ dosomeing}
- 调用
HandleFunc()
注册处理器函数 - 调用
ListenAndServer()
监听localhost:8080
并提供服务
HandleFunc()与Handle()实现原理
调用档次
http.HandleFunc()
与http.Handle()
的底层实现都依赖于ServerMux.Handle()
。http.HandleFunc()
本人对ServeMux.Handle()
进行了封装,封装为ServeMux.HandleFunc()
并定义了本人的数据结构HandlerFunc
该构造是函数类型并且实现了Handler
接口。Mux.Handle()
承受一个类型为Handler
的参数,所以调用mux.Handle()
必须实现Handler
接口。http.HandleFunc()
通过本人的数据结构HandlerFunc
实现了Handler
接口所以调用http.HandleFunc()
时不必本人去实现Handler
接口,间接向http.HandleFUnc()
传入pattern string
和func(w http.ResponseWriter, r *http.Request){}
即可;http.Handle()
间接调用Mux.Handle()
所以须要调用者本人去定义一个数据结构并且该数据结构要实现Handler
接口,在应用该数据结构前对其进行初始化,而后将pattern string
与该数据结构传入http.Handle()
。
实现
// Handle registers the handler for the given pattern// in the DefaultServeMux.// The documentation for ServeMux explains how patterns are matched.func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }// 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)}
当咱们调用 http.HandleFunc()
注册处理器时,规范库会应用DefaultServeMux
默认的 HTTP 服务器 解决申请。
type ServeMux struct { mu sync.RWMutex m map[string]muxEntry // 处理器键值对map es []muxEntry // slice of entries sorted from longest to shortest.从最长到最短排序的entries切片 hosts bool // whether any patterns contain hostnames 是否有任何模式蕴含主机名}type muxEntry struct { h Handler pattern string}// /DefaultServeMux 是 Serve 应用的默认 ServeMuxvar DefaultServeMux = &defaultServeMuxvar defaultServeMux ServeMux
http.HandleFunc()
在底层实现上与http. Handle()
一样,都应用Handle
办法。不同的是http.HandleFunc()
在调用Handle
前须要对handler
进行强制转化为HandlerFunc
,HandlerFunc
实现了ServeHTTP
办法,而Handle()
须要本人实现ServeHTTP
办法。
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { if handler == nil { panic("http: nil handler") } // 强制转换 将handler转换为HandlerFunc mux.Handle(pattern, HandlerFunc(handler))}
HandlerFunc
以函数类型func(ResponseWriter, *Request)
为底层类型,为HandlerFunc
实现了ServeHTTP
办法即HandlerFunc
实现了Handler
接口。Go语言只有某个数据结构实现了接口的办法集就实现了这个接口。
只有你的handler函数签名是:
func (ResponseWriter, *Request)
那么这个handler
和http.HandlerFunc()
就有了统一的函数签名,能够将该handler()
函数进行类型转换,转为http.HandlerFunc
。而http.HandlerFunc
实现了http.Handler
这个接口。在http
库须要调用你的handler函数来解决http申请时,会调用HandlerFunc()
的ServeHTTP()
函数,可见一个申请的根本调用链是这样的
h = getHandler() => h.ServeHTTP(w, r) => h(w, r)
// The HandlerFunc type is an adapter to allow the use of// ordinary functions as HTTP handlers. If f is a function// with the appropriate signature, HandlerFunc(f) is a// Handler that calls f.type HandlerFunc func(ResponseWriter, *Request)// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r)}
HandlerFunc
类型只是为了不便注册函数类型的处理器。咱们能够间接申明一个实现了Handler
接口的数据结构,而后应用http.Handle()
注册该数据结构的示例:
package mainimport ( "fmt" "net/http")// func main() {// http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {// fmt.Fprintln(w, "Hello World")// })// http.ListenAndServe("localhost:8080", nil)// }type Hello struct{}func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello World")}func main() { // http.Hangle承受两个参数:pattern string, handler Handler // 第二参数为handler,咱们本人定义一个实现了接口handler的数据结构,在这里须要对该数据结构进行初始化。 // h := Hello{} // http.Handle("/hello", h) http.Handle("/hello", Hello{}) http.ListenAndServe("localhost:8080", nil)}
为了辨别,咱们将通过HandleFunc()
注册的称为处理函数,通过Handle
注册的称为处理器。官网解释为Handle registers the handler for the given pattern in the DefaultServeMux
HandleFunc registers the handler function for the given pattern in the DefaultServeMux
Handler
响应 HTTP 申请,Handler
为处理器函数。
type Handler interface { ServeHTTP(ResponseWriter, *Request)}
Handle
办法只承受类型为接口Handler
的参数。Handle
依据pattern
参数在ServeMux
(多路复用器)注册处理器函数。ServeMux
依据最长匹配准则进行匹配Handler
的pattern
,所以在注册时须要对pattern
进行排序。
// 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 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) } 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 }}func appendSorted(es []muxEntry, e muxEntry) []muxEntry { n := len(es) i := sort.Search(n, func(i int) bool { return len(es[i].pattern) < len(e.pattern) }) if i == n { return append(es, e) } // we now know that i points at where we want to insert es = append(es, muxEntry{}) // try to grow the slice in place, any entry works. copy(es[i+1:], es[i:]) // Move shorter entries down es[i] = e return es}
ListebAndServe
调用档次
实现
ListenAndServe
监听 TCP 网络地址 addr,而后调用 handler
来解决传入连贯上的申请。 承受的连贯被配置为启用 TCP keep-alives。 handler
通常为为 nil,在这种状况下应用 DefaultServeMux。 ListenAndServe
总是返回一个非零谬误。
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe()}
ListenAndServe
创立了一个Server
。Server
定义了运行 HTTP 服务器的参数。Server
的零值是无效配置。咱们能够应用这些字段来调节 Web 服务器的参数。
type Server struct { // Addr 可选地指定服务器要监听的 TCP 地址, // 以"host:port"的模式。如果为空,则应用":http" (port 80) Addr string // 要调用的handler,如果为 nil,则为 http.DefaultServeMux Handler Handler // 通过ServerTLS和ListenAndServerTLS TLSConfig 可选地提供一个 TLS 配置以供应用 TLSConfig *tls.Config ReadTimeout time.Duration ReadHeaderTimeout time.Duration WriteTimeout time.Duration IdleTimeout time.Duration MaxHeaderBytes int TLSNextProto map[string]func(*Server, *tls.Conn, Handler) ConnState func(net.Conn, ConnState) ErrorLog *log.Logger BaseContext func(net.Listener) context.Context ConnContext func(ctx context.Context, c net.Conn) context.Context inShutdown atomicBool disableKeepAlives int32 nextProtoOnce sync.Once nextProtoErr error mu sync.Mutex listeners map[*net.Listener]struct{} activeConn map[*conn]struct{} doneChan chan struct{} onShutdown []func()}
//ListenAndServe 侦听 TCP 网络地址 srv.Addr//调用handler解决传入连贯上的申请。//承受的连贯配置为启用TCP keep-alives。//如果 srv.Addr 为空,则应用 ":http"。//ListenAndServe 总是返回一个非零谬误。 //Shutdown 或 Close 后,返回的谬误为 ErrServerClosed。func (srv *Server) ListenAndServe() error { if srv.shuttingDown() { return ErrServerClosed } addr := srv.Addr if addr == "" { addr = ":http" } // func net.Listen(network string, address string) (net.Listener, error) // 监听本地网络地址上的通告。 // network为"tcp","tcp4","tcp6","unix" 或 "unixpacket"。 ln, err := net.Listen("tcp", addr) if err != nil { return err } // func (srv *Server) Serve(l net.Listener) error return srv.Serve(ln)}
在该办法中,先调用net.Listen()
监听端口,将返回的net.Listener
作为参数调用Server.Serve()
办法
//Serve 承受 Listener l 上的传入连贯,为每个连贯创立一个新的服务 goroutine。服务 goroutine 读取申请,而后调用 srv.Handler 来回复它们。////仅当侦听器返回 *tls.Conn 连贯并且它们在 TLS 中配置为“h2”时才启用 HTTP/2 反对//Config.NextProtos。////Serve 总是返回一个非 nil 谬误并敞开 l。//Shutdown 或 Close 后,返回的谬误为 ErrServerClosed。func (srv *Server) Serve(l net.Listener) error { // 未包装的listener 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循环,不停地调用Listener.Accept()办法承受新连贯,开启新 goroutine 解决新连贯 for { rw, err := l.Accept() if err != nil { select { case <-srv.getDoneChan(): return ErrServerClosed default: } // 指数退却策略 // 如果l.Accept()调用返回谬误,咱们判断该谬误是不是临时性地(ne.Temporary())。如果是临时性谬误,Sleep一小段时间后重试,每产生一次临时性谬误,Sleep的工夫翻倍,最多Sleep 1s 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 // 取得新连贯后,将其封装成一个conn对象 c := srv.newConn(rw) c.setState(c.rwc, StateNew, runHooks) // before Serve can return go c.serve(connCtx) }}
serve()
办法不停地读取客户端发送地申请,创立serverHandler
对象调用其ServeHTTP()
办法去解决申请,而后做一些清理工作。
//提供新的连贯。func (c *conn) serve(ctx context.Context) { for { w, err := c.readRequest(ctx) serverHandler{c.server}.ServeHTTP(w, w.req) w.finishRequest() }}
serverHandler
只是一个两头的辅助构造
要害代码
type serverHandler struct { srv *Server}func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler handler.ServeHTTP(rw, req)}
//serverHandler 委托给服务器的 Handler 或//DefaultServeMux 并且还解决“OPTIONS *”申请。type serverHandler struct { srv *Server}func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { // 从Server对象中获取Handler,这个Handler就是调用http.ListenAndServe()时传入的第二个参数 handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") { var allowQuerySemicolonsInUse int32 req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() { atomic.StoreInt32(&allowQuerySemicolonsInUse, 1) })) defer func() { if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 { sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192") } }() } handler.ServeHTTP(rw, req)}
//ServeHTTP 将申请分派给其模式与申请 URL 最匹配的处理程序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 } // mux.Handler(r)通过申请的门路信息查找处理器 h, _ := mux.Handler(r) // 调用处理器的ServeHTTP()办法解决申请 h.ServeHTTP(w, r)}
Handler()
通过URL信息查找handler
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { // 剥离端口 // stripHostPort 返回 h,不带任何尾随 ":<port>" host := stripHostPort(r.Host) return mux.handler(host, r.URL.Path)}
handler
是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}
在给定门路字符串的handler map上查找handler,最长匹配模式。
func (mux *ServeMux) match(path string) (h Handler, pattern string) { // Check for exact match first. // 首先查看齐全匹配。 v, ok := mux.m[path] if ok { return v.h, v.pattern } // Check for longest valid match. mux.es contains all patterns // that end in / sorted from longest to shortest. // 查看最长的无效匹配。 mux.es 蕴含所有以 /结尾的模式,从最长到最短排序。 // 只有注册了/根门路解决,所有未匹配到的门路最终都会交给/门路解决 // 所以在注册handler时须要对pattern进行排序从最长到最短 for _, e := range mux.es { // func strings.HasPrefix(s string, prefix string) bool // HasPrefix 测试字符串 s 是否以prefix结尾。 if strings.HasPrefix(path, e.pattern) { return e.h, e.pattern } } return nil, ""}
ServeMux
示例
http.HandleFunc()/http.Handle()
都是将处理器/函数注册到ServeMux
的默认对象DefaultServeMux
上。应用DefaultServeMux
不可控。
一来Server
参数都应用了默认值,二来第三方库也可能应用这个默认对象注册一些解决,容易抵触。更重大的是,咱们在不知情中调用http.ListenAndServe()
开启 Web 服务,那么第三方库注册的解决逻辑就能够通过网络拜访到,有极大的安全隐患。所以,除非在示例程序中,否则倡议不要应用默认对象。
咱们能够应用http.NewServeMux()
创立一个新的ServeMux
对象,而后创立http.Server
对象定制参数,用ServeMux
对象初始化Server
的Handler
字段,最初调用Server.ListenAndServe()
办法开启 Web 服务:
func main() { mux := http.NewServeMux() mux.HandleFunc("/", index) mux.Handle("/hello", Hello(struct{})) server := &http.Server{ Addr: ":8080", Handler: mux, ReadTimeout: 20 * time.Second, WriteTimeout: 20 * time.Second, } server.ListenAndServe()}
实现
NewServeMux()
调配并返回一个新的ServeMux
// func new(Type) *Type//新的内置函数分配内存。第一个参数是一个类型, 不是一个值,返回的值是一个指向新的指针 调配了该类型的零值。func NewServeMux() *ServeMux { return new(ServeMux) }
ServeMux
实现了ServeHTTP
办法即ServeMux
实现了Handler
,故在设置server
时能够将ServeMux
当作HAndler
传入。Server.Handler = nil
为DefaultServeMux
,Server.Handler = mux
时应用调用者定义的多路复用器。
中间件(middleware)
中间件对申请进行一些预处理或后处理。它位于Go web服务器和处理器函数之间。
任何办法实现了ServeHTTP
,即是一个非法的http.Handler
。
应用中间件能够剥离非业务逻辑。
中间件通过函数闭包实现。
中间件通过包装handler
再返回一个handler
。
func hello(wr http.ResponseWriter, r *http.Request) { wr.Write([]byte("hello"))}func timeMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(wr http.ResponseWriter, r *http.Request) { timeStart := time.Now() // next handler next.ServeHTTP(wr, r) timeElapsed := time.Since(timeStart) logger.Println(timeElapsed) })}func main() { http.Handle("/", timeMiddleware(http.HandlerFunc(hello))) err := http.ListenAndServe(":8080", nil) ...}
customizedHandler = logger(timeout(ratelimit(helloHandler)))
函数链执行过程中的上下文:
链接中间件
customizedHandler = logger(timeout(ratelimit(helloHandler)))
写法太繁琐,咱们能够进行简化。
r = NewRouter()r.Use(logger)r.Use(timeout)r.Use(ratelimit)r.Add("/", helloHandler)
能够如果应用Use()
来减少或删除中间件。
实现
type middleware func(http.Handler) http.Handlertype Router struct { middlewareChain [] middleware mux map[string] http.Handler}func NewRouter() *Router{ return &Router{}}// 承受中间件切片func (r *Router) Use(m middleware) { r.middlewareChain = append(r.middlewareChain, m)}// 运行m1(m2(m3(m4(m5(handler)))))func (r *Router) Add(route string, h http.Handler) { var mergedHandler = h // for 循环将从中间件的 Slice中循环遍历中间件,并将其本身包裹在传递给函数的 mergedHandler四周。 for i := len(r.middlewareChain) - 1; i >= 0; i-- { mergedHandler = r.middlewareChain[i](mergedHandler) } r.mux[route] = mergedHandler}
参考:
Go 每日一库之 net/http(根底和中间件)
go高级编程