go語言可以用幾行代碼搭建一個http服務器,例如:
package main
import (
"fmt"
"net/http"
)
func SayHello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
}
func main() {
http.HandleFunc("/", SayHello)
http.ListenAndServe(":8080", nil)
}
也可以自定義多路複用器和http.Server搭建一個http服務器:
package main
import (
"fmt"
"net/http"
)
func SayHello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
}
func main() {
// 創建一個多路複用器
mux := http.NewServeMux()
// 綁定pattern和Handler函數
mux.HandleFunc("/", SayHello)
// 創建server 綁定多路複用器
server := http.Server{
Addr: ":8080",
Handler: mux,
TLSConfig: nil,
ReadTimeout: 0,
ReadHeaderTimeout: 0,
WriteTimeout: 0,
IdleTimeout: 0,
MaxHeaderBytes: 0,
TLSNextProto: nil,
ConnState: nil,
ErrorLog: nil,
BaseContext: nil,
ConnContext: nil,
}
// 開始監聽
server.ListenAndServe()
}
搭建服務器的準備:
從上面的代碼可以看到go語言的http服務器有三個必需對象:handler函數(SayHello)、多路複用器(mux)和httpServer(server)。
先來看看從handler函數到服務器啓動的過程,首先創建一個和http.HandlerFunc簽名相同的handler函數SayHello,也可以是實現http.Handler接口的類型的對象。然後創建一個多路複用器,mux := http.NewServeMux()。
再調用mux.HandleFunc("/", SayHello)函數綁定路由和handler函數的關係。mux.HandleFunc("/", SayHello)後面做了2件事。1,把函數SayHello轉成HandlerFunc類型;2,調用mux.Handle(pattern string, handler Handler)方法把"/"和SayHello存儲到mux內部的map對象和切片對象中。
mux.HandleFunc("/", SayHello)方法的源碼
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
mux.Handle(pattern, HandlerFunc(handler))的源碼
// 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
}
}
然後創建相當於套接字功能的server對象,初始化server的Handler屬性爲mux,最後調用server.ListenAndServe()就完成了http服務器的最簡單的搭建。
服務器處理請求的過程:
server.ListenAndServe()方法創建了一個套接字對象,並調用server.Serve(l net.Listener) 方法監聽該套接字。
// ListenAndServe always returns a non-nil error. After Shutdown or Close,
// the returned error is ErrServerClosed.
func (srv *Server) ListenAndServe() error {
if srv.shuttingDown() {
return ErrServerClosed
}
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(ln)
}
在server.Serve(l net.Listener)方法內部有個死循環一直監聽套接字,一旦有數據被接收就會創建一個連接c,並調用創建一個 serve(ctx context.Context)函數協程。
// Serve always returns a non-nil error and closes l.
// After Shutdown or Close, the returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {
// 代碼太長 省略
for {
rw, e := l.Accept()
if e != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
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
}
connCtx := ctx
if cc := srv.ConnContext; cc != nil {
connCtx = cc(connCtx, rw)
if connCtx == nil {
panic("ConnContext returned nil")
}
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(connCtx)
}
}
在c.serve(ctx context.Context)方法中調用了serverHandler{c.server}.ServeHTTP(w, w.req),這裏把c.server即上面文章前面搭建服務器的準備中介紹的相當於套接字功能的server對象,serverHandler類型實現了Handler接口。
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
// 源碼太長 省略掉其他部分
serverHandler{c.server}.ServeHTTP(w, w.req)
// 源碼太長 省略掉其他部分
}
看一下serverHandler.ServeHTTP(w,w.req)方法源碼,handler := sh.srv.Handler,handler其實是server.Handler,還記得在創建server對象時初始化的Handler屬性值嗎?就是mux(多路複用器),這個方法最終調用的是mux.ServeHTTP(rw, req)。
// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
srv *Server
}
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)
}
在mux.ServeHTTP(rw, req)內部調用了mux.Handler(r)方法得到handler函數對象,並執行這個函數。
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request 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
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
至此一個請求的基本過程就完成了,但還有兩點要解釋。1,mux.Handler(r)根據request請求返回handler函數時調用了mux.handler(host, r.URL.Path),在mux.handler(host, r.URL.Path)內部又調用了mux.match(path)方法,最終這個match(path string) (h Handler, pattern string)方法纔是根據請求url查詢handler函數的重點。
// If there is no registered handler that applies to the request,
// Handler returns a ``page not found'' handler and an empty pattern.
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// 代碼太長 省略
return mux.handler(host, r.URL.Path)
}
// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
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
}
match(path string) (h Handler, pattern string)方法是從多路複用器mux內部map對象和切片對象尋找相同的請求路徑或者長得像的請求路徑,並返回handler函數,這裏的請求路徑和handler函數就是上文調用mux.HandleFunc("/", SayHello)綁定的請求路徑和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.
for _, e := range mux.es {
if strings.HasPrefix(path, e.pattern) {
return e.h, e.pattern
}
}
return nil, ""
}
2,由於上文中我使用的是mux.HandleFunc("/", SayHello)綁定請求路徑和handler函數,所以調用mux.Handler(r)返回的其實是一個http.HandlerFunc類型的函數,並且http.HandlerFunc也實現了Handler接口,可以看到http.HandlerFunc的ServeHTTP(w ResponseWriter, r *Request)方法實現的是調用自身,即綁定的handler函數。
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
參考一下思維導圖
附本文參考書籍:
參考文獻:https://github.com/ffhelicopter/Go42/blob/master/content/42_36_http.md