go 语言 http 服务器的实现

dongkui0712 · · 1343 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

本文总结分析 go 1.9 http 服务器的实现 # hello world 可以用如下代码实现简单的 go 语言 HTTP 服务的 hello world: ```go package main import ( "io" "net/http" "log" ) // hello world, the web server func HelloServer(w http.ResponseWriter, req *http.Request) { io.WriteString(w, "hello, world!\n") } func main() { http.HandleFunc("/hello", HelloServer) log.Fatal(http.ListenAndServe(":12345", nil)) } ``` 最简单的 golang HTTP 服务器由 `http.HandleFunc(pattern string, handler func(ResponseWriter, *Request))` 做路由分发, `http.ListenAndServe(addr, hanlder)` 开启监听并启动服务。 ## http.HandleFunc ``` func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) } ``` HandleFunc 函数在 DefaultServeMux 中为指定的 pattern 注册 HTTP 服务处理函数 func(ResponseWriter, *Request) ,即由该 handler 处理对 pattern 的请求。 ## http.ListenAndServe 代码如下: ``` func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() } ``` ListenAndServe 监听 TCP 地址,调用 Server 处理该连接中的请求。如果 handler 为空,则使用 DefaultServeMux。 该函数,其实是默认 Server 的 ListenAndServe 方法的封装。 # ServeMux ServeMux 是一个 HTTP 请求多路转接器。它将收到的 HTTP 请求的 URL 和已经注册的 pattern 做匹配,并调用最匹配的 pattern 对应的 hanlder 来处理该请求。 pattern 是固定的、以跟路径符号 `/` 开始的路径,如 "/favicon.ico"。也可以是跟路径的子树,如 "/images/"。匹配时,长 pattern 优先级更高。 以 `/` 结尾的 pattern 定义了一个子树。`/` 本身可以匹配任何 pattern。 如果已经注册了一个如 `/images/` 的子树 pattern,接收到一个如 `/images` 的不带最后 `/` 符号的请求时,ServeMux 会将该请求重定向到子树 pattern `/images/` 对应的处理器 handler(除非 `/images` 被单独注册)。 pattern 可以可选地指定域名。指定域名的 pattern 只会匹配该域名的 URL。指定域名的 pattern 优先级高于未指定的。 ServeMux also takes care of sanitizing the URL request path,redirecting any request containing . or .. elements or repeated slashes to an equivalent, cleaner URL. ``` type ServeMux struct { mu sync.RWMutex m map[string]muxEntry hosts bool // whether any patterns contain hostnames } type muxEntry struct { explicit bool h Handler pattern string } ``` ServeMux 的 ServeHTTP 方法分发 HTTP 请求到与其 URL 匹配度最高的 pattern 注册的处理器 h:mux.Handler(r) 返回匹配度最高的处理器 h,然后调用 h.ServeHTTP(w, r) 处理请求。 ``` 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) } ``` # Server Server 结构体定义启动 HTTP 服务器的参数 ``` type Server struct { Addr string Handler Handler 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 } ``` 此处的 Handler 是个接口,意味着只要实现 ServeHTTP(ResponseWriter, *Request) 方法,就能作为 Server 的 Handler: ``` type Handler interface { ServeHTTP(ResponseWriter, *Request) } ``` ## ListenAndServe 方法 ListenAndServe 方法监听 Addr 字段指定的 TCP 连接,处理请求。接收的连接激活了 TCP keep-alives。如果 Addr 为空,则使用 ":http"。总是返回非空 error。 ``` 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)}) } ``` ListenAndServe 方法的实现主要有两个步骤: - net.Listen("tcp", addr) 得到 net.Listener - Serve 方法并发处理监听到的请求 下面看看 Serve 方法实现的主要步骤(只保留主要逻辑): ``` func (srv *Server) Serve(l net.Listener) error { defer l.Close() ... baseCtx := context.Background() // base is always background, per Issue 16220 ctx := context.WithValue(baseCtx, ServerContextKey, srv) for { rw, _ := l.Accept() c := srv.newConn(rw) go c.serve(ctx) } } ``` 最终,serve 方法调用 Server 中的 Handler 的 ServeHTTP 方法处理该请求。 # 注册函数 Handle 和 HandleFunc 将 HTTP 请求处理函数注册到 ServeMux 中 ``` func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) } ``` ``` func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) } ``` 使用方式略有差别。 `HandleFunc(pattern, handler)` 与 `Handle(pattern, HandlerFunc(handler))` 效果一样。例如: ``` http.Handle("/foo", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) })) http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }) ``` 也可以从 ServeMux.HandleFunc 的实现看出来: ``` func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler)) } ``` # 比较 http 包有几个概念易混,特别分析对比一下。 ## HandlerFunc HandlerFunc 是一种类型,是以 func(ResponseWriter, *Request) 申明的函数。是个将普通函数转化为 HTTP 处理器的适配器。 ``` type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } ``` 用法: 定义普通函数 handlerFunc 如下,HandlerFunc(f) 即是一个调用 f 的 HTTP 处理器。 ``` func f(w http.ResponseWriter, r *http.Request) { ... } ``` ## Handler Handler 是个接口。功能如其名:处理器。 http 包中实现 ServeHTTP(ResponseWriter, *Request) 方法即拥有处理 HTTP 请求的能力。 ``` type Handler interface { ServeHTTP(ResponseWriter, *Request) } ``` HandlerFunc 类型和 ServeMux 结构体都实现了 ServeHTTP(w ResponseWriter, r *Request) 方法, 也就是说,HandlerFunc 类型和 ServeMux 结构体都是一种 Handler。 HandlerFunc 的 ServeHTTP 方法直接调用自身,处理 HTTP 请求。 ServeMux 调用与请求路径匹配的 `func(ResponseWriter, *Request)` 类型的处理函数。 同样的,我们可以自己定义一个实现 ServeHTTP(ResponseWriter, *Request) 方法的结构体,定制我们自己的 HTTP 处理器。第三方的路由处理器(如 mux, httprouter 等)都是 Handler 的一种实现。 Handle 和 HandleFunc 的作用就是将 HTTP 请求处理函数注册到 ServeMux 中。

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1343 次点击  
加入收藏 微博
上一篇:golang
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传