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高级编程
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。