net/http
web 示例:
package main
import (
"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 应用的默认 ServeMux
var DefaultServeMux = &defaultServeMux
var 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 main
import (
"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.Handler
type 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 高级编程