go 理解HTTP服务

105 阅读4分钟

初识HTTP服务

func indexHandler(w http.ResponseWriter, req *http.Request) {
   fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)
}
func main() {
   http.HandleFunc("/", indexHandler)
   err := http.ListenAndServe("9999", nil)
   log.Fatal(err)
}

http.HandleFunc()

保存路由(pattern)与处理函数(handler)之间的映射关系到ServeMux中

// 注册pattern与handler之间的映射关系
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

DefaultServeMux本质是ServeMux,故DefaultServeMux.HandleFunc()实质是ServeMux.HandleFunc()

var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
type muxEntry struct {
   h       Handler
   pattern string
}
type ServeMux struct {
    mu    sync.RWMutex   // 读写锁
    m     map[string]muxEntry // map,存储handler与pattern之间的映射关系
    es    []muxEntry 
    hosts bool       
}
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    // 如果handler为空,直接退出程序。因为每一个pattern都应该对应一个handler
    if handler == nil {
	panic("http: nil handler")
    }
    // 调用ServeMux.Handle, 并将handler类型转换为HandlerFunc。
    mux.Handle(pattern, HandlerFunc(handler))
}
func (mux *ServeMux) Handle(pattern string, handler Handler) {
   // 使用了ServeMux结构体中的写锁
   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)
   }
   // 初始化一个muxEntry,并添加到map中
   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
   }
}

http.ListenAndServe()

1、建立tcp连接监听端口 2、可以自定义服务分发器(将请求的url转发到对应的handler进行处理),如果不自定义则会有默认的服务分发器(DefaultServeMux)

// 监听TCP网络地址addr,然后调用Serve来处理传入连接的请求
func ListenAndServe(addr string, handler Handler) error {
   server := &Server{Addr: addr, Handler: handler}
   return server.ListenAndServe()
}
// Server定义了运行一个HTTP服务器的参数
type Server struct {
    Addr string // tcp连接的端口号
    Handler Handler // 分发路由的Handler
    ......
}
func (srv *Server) ListenAndServe() error {
    ......
    // tcp的端口号
    addr := srv.Addr
    if addr == "" {
	addr = ":http"
    }
    // 监听addr端口
    ln, err := net.Listen("tcp", addr)
    if err != nil {
	return err
    }
    return srv.Serve(ln)
}
func (srv *Server) Serve(l net.Listener) error {
   ......
   ctx := context.WithValue(baseCtx, ServerContextKey, srv)
   // 无限循环接受连接,并为每一个连接开启一个goroutine进行处理
   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
      // 开启一个goroutine处理
      go c.serve(connCtx)
   }
}
func (c *conn) serve(ctx context.Context) {
    ......
    inFlightResponse = w
    // 调用handler函数,将pattern分发到对应的处理函数(handleFunc)。
    //初始化serverHandler,serverHandler实现了ServeHTTP接口
    serverHandler{c.server}.ServeHTTP(w, w.req)
    inFlightResponse = nil
    ......
}
type serverHandler struct {
   srv *Server
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
   // 取出Server的handler
   handler := sh.srv.Handler
   // 如果没有自定义的handler(http.ListenAndServe中的第二个参数,这个参数的作用是处理路由分发),
   //就使用DefaultServeMux作为默认的服务分发器,即将pattern与handler匹配起来
   if handler == nil {
      handler = DefaultServeMux
   }
   ......
   // 调用handler的ServeHTTP
   handler.ServeHTTP(rw, req)
}
// 由于使用了DefaultServeMux,所以handler.ServeHTTP()就是DefaultServeMux.ServeHTTP()
// 又因为DefaultServeMux==ServeMux,所以最终调用的是ServeMux.ServeHTTP()
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
   }
   // 根据r中的url,在map中找到handler
   h, _ := mux.Handler(r)
   // 调用handler.ServeHTTP()
   h.ServeHTTP(w, r)
}

代码一直到最后,h指的是ServeMux中m的muxEntry的handler,此处的handler是http.HandleFunc()第二个参数,也就是最开始代码中的indexHandler,此处存在一个疑问,indexHandler为什么能调用ServeHTTP这个接口?此处并没有真正调用muxEntry中的handler,也就是路由过来的请求并没有被处理。

// Handler接口中只有一个ServeHTTP方法,上述代码中调用的ServeHTTP都是实现的Handler接口
type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

// 定义func(ResponseWriter, *Request)这一函数类型为HandlerFunc,也就是说只要是
// 函数的参数是func(ResponseWriter, *Request)这种类型的都是HandlerFunc
type HandlerFunc func(ResponseWriter, *Request)

// HandlerFunc实现了ServeHTTP方法,也就是只要函数为func(ResponseWriter, *Request)
// 此种类型的都默认实现了Handler接口,也就可以使用ServeHTTP方法。
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    // HandlerFunc此种类型的函数在调用ServeHTTP时,都会调用f(w, r)
    // 在本文表现为,indexHandler为HandlerFunc类型的函数,调用了f(w, r),也就是调用了
    //indexHandler(w ResponseWriter, r *Request),即将路由过来的请求按照indexHandler()进行处理
    f(w, r)
}

总结

设计方法

定义函数为一种类型,函数实现接口的方法,可以回调函数。

流程总结

  • 调用http.HandleFunc()
  1. 调用DefaultServeMux 的 HandleFunc,
  2. 调用了 DefaultServeMux 的 Handle
  3. 向DefaultServeMux的map中增加对应的handler和pattern的映射关系
  • 调用http.ListenAndServe()
  1. 初始化Server,存储监听的端口号以及设置路由规则
  2. 调用Server.ListenAndServe()
  3. 调用net.Listen()监听端口
  4. 开启无限循环,从队列种拿Accpet
  5. 对每一个Accept初始化一个连接,并开启一个goroutine
  6. 读取请求内容
  7. 判断handler是否为空,如果为空就设置handler为DefaultServeMux
  8. 调用handler.ServeHttp(),即根据路由规则调用处理函数