Go HTTP Server 学习笔记

概述

在 Go 中启动一个最简单的 HTTP Server 的代码如下示例地址:

package main

import (
	"io"
	"log"
	"net/http"
)

func main() {
	// Hello world, the web server

	helloHandler := func(w http.ResponseWriter, req *http.Request) {
		io.WriteString(w, "Hello, world!\n")
	}

	http.HandleFunc("/hello", helloHandler)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

启动流程分析

  • 第一层,标准库创建 HTTP 服务是通过创建一个 Server(实质是调用了 server.ListenAndServe())。
    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 先定义了 net.Listen() 然后 调用了 Serve() 方法。
    func (srv *Server) Serve(l net.Listener) error {
    	
      // 略
      for {
          rw, err := l.Accept()
          if err != nil {
              select {
              case <-srv.getDoneChan():
                  return ErrServerClosed
              default:
              }
              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
          c := srv.newConn(rw)
          c.setState(c.rwc, StateNew, runHooks) // before Serve can return
          go c.serve(connCtx)
      }
    }
    
  • 第三层,Serve方法,通过 for 循环接受请求,每个连接默认开启一个 Goroutine 为其服务。
  • 第四、五层,serverHandler 结构代表请求对应的处理逻辑,并且通过这个结构进行具体业务逻辑处理。 代码太长了,只贴出最核心的部分serverHandler{c.server}.ServeHTTP(w, w.req)
  • 第六层,Server 数据结构如果没有设置处理函数 Handler,默认使用 DefaultServerMux 处理请求。
// 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)
}
  • 第七层,DefaultServerMux 是使用 map 结构来存储和查找路由规则。
// DefaultServeMux is the default ServeMux used by Serve.

var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux

// ServeMux is an HTTP request multiplexer.
// It matches the URL of each incoming request against a list of registered
// patterns and calls the handler for the pattern that
// most closely matches the URL.

type ServeMux struct {
	mu    sync.RWMutex
	m     map[string]muxEntry
	es    []muxEntry // slice of entries sorted from longest to shortest.
	hosts bool       // whether any patterns contain hostnames
}