Golang http请求源码分析


| 阅读 |,阅读约 4 分钟
| 复制链接:

Overview

Golang http请求源码分析

go提供的标准库net/http,实现一个简单的http server非常容易,只需要短短几行代码。本篇文章将会对go标准库net/http实现http服务的原理进行较为深入的探究

快速搭建http server服务

搭建http server的大概步骤包括:

  • 编写handler处理函数
  • 注册路由
  • 创建服务并开启监听
 1package main
 2
 3import (
 4  "io"
 5  "log"
 6  "net/http"
 7)
 8
 9// 请求处理函数
10func indexHandler(w http.ResponseWriter, r *http.Request) {
11  _, _ = io.WriteString(w, "hello, world!\n")
12}
13
14func main() {
15  // 注册路由
16  http.HandleFunc("/", indexHandler)
17  // 创建服务并开启监听
18  err := http.ListenAndServe(":8001", nil)
19  if err != nil {
20    log.Fatal("ListenAndServe: ", err)
21  }
22}

http服务处理流程

  • 请求会先进入路由
  • 路由为请求找到合适的handler
  • handler对request进行处理,并构建response

http服务处理流程

Golang的http包处理流程

  • 路由处理的核心对象是ServeMux
  • ServeMux内部维护一个map属性,保存了路由路径和路由处理函数的映射关系
  • 注册路由时,往map中写入数据
  • 匹配路由时,从map中找到合适的handler处理

go-http处理流程

关键源码逻辑

下图展示的源码中的关键逻辑:

高清地址

golang http源码关键逻辑

路由注册接口

共有两个函数可以用于路由注册,底层都调用的是DefaultServeMux

源码位置:src/net/http/server.go

 1type Handler interface {
 2  ServeHTTP(ResponseWriter, *Request)
 3}
 4
 5func Handle(pattern string, handler Handler) {
 6  DefaultServeMux.Handle(pattern, handler)
 7}
 8
 9func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
10  DefaultServeMux.HandleFunc(pattern, handler)
11}

路由实现

go中的路由基于ServeMux结构实现

 1type ServeMux struct {
 2  mu    sync.RWMutex
 3  // 存储路由和handler的对应关系
 4  m     map[string]muxEntry
 5  // 将muxEntry排序存放,排序按照路由表达式由长到短排序
 6  es    []muxEntry
 7  // 路由表达式是否包含主机名
 8  hosts bool
 9}
10
11type muxEntry struct {
12  // 路由处理函数
13  h       Handler
14  // 路由表达式
15  pattern string
16}
17

路由注册逻辑

  • go提供了默认的路由实例DefaultServeMux,如果用户没有自定义路由,就用这个默认的路由
  • 添加路由函数的核心逻辑:将表达式作为key,路由处理函数和表达式组成的muxEntry作为value保存到map中
 1// 服务启动后的默认路由实例
 2var DefaultServeMux = &defaultServeMux
 3
 4// 前面demo中调用handle的内部逻辑
 5func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
 6  DefaultServeMux.HandleFunc(pattern, handler)
 7}
 8
 9// HandleFunc
10func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
11  ...
12  mux.Handle(pattern, HandlerFunc(handler))
13}
14
15// Handle
16func (mux *ServeMux) Handle(pattern string, handler Handler) {
17  ...
18  // 创建ServeMux的m实例
19  if mux.m == nil {
20    mux.m = make(map[string]muxEntry)
21  }
22  // 根据路由表达式和路由处理函数,构造muxEntry对象
23  e := muxEntry{h: handler, pattern: pattern}
24  // muxEntry保存到map中
25  mux.m[pattern] = e
26
27  // 如果表达式以 '/' 结尾,加入到排序列表中
28  if pattern[len(pattern)-1] == '/' {
29    mux.es = appendSorted(mux.es, e)
30  }
31
32  if pattern[0] != '/' {
33    mux.hosts = true
34  }
35}

开启服务

核心逻辑包括:监听端口、等待连接、创建连接、处理请求

 1// 开启服务的入口
 2func ListenAndServe(addr string, handler Handler) error {
 3  // 创建一个Server,传入handler
 4  // 我们的例子中handler为空
 5  server := &Server{Addr: addr, Handler: handler}
 6  // 调用ListenAndServe真正监听
 7  return server.ListenAndServe()
 8}
 9
10// ListenAndServe
11func (srv *Server) ListenAndServe() error {
12  ...
13  ln, err := net.Listen("tcp", addr)
14  return srv.Serve(ln)
15}
16
17func (srv *Server) Serve(l net.Listener) error {
18  ...
19  // for循环
20  for {
21    // 创建上下文对象
22    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
23    // 等待新的连接建立
24    rw, err := l.Accept()
25    ...
26    // 连接建立时,创建连接对象
27    c := srv.newConn(rw)
28    c.setState(c.rwc, StateNew) // before Serve can return
29    // 创建协程处理请求
30    go c.serve(connCtx)
31  }
32}

处理请求

处理请求的逻辑主要是:根据路由请求去和ServeMux的m做匹配,找到合适的handler

 1func (c *conn) serve(ctx context.Context) {
 2  ...
 3  for {
 4    // 读取下一个请求进行处理(所有的请求都在该协程中进行)
 5    w, err := c.readRequest(ctx)
 6    ...
 7
 8    // 内部转调ServeHTTP函数
 9    serverHandler{c.server}.ServeHTTP(w, w.req)
10    ...
11  }
12}
13
14// ServeHTTP
15func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
16  // sh.srv.Handler是前面的http.ListenAndServe(":8001", nil)传入的handler
17  handler := sh.srv.Handler
18  // 如果handler为空,就用默认的DefaultServeMux
19  if handler == nil {
20    handler = DefaultServeMux
21  }
22  if req.RequestURI == "*" && req.Method == "OPTIONS" {
23    handler = globalOptionsHandler{}
24  }
25  // 这里就是调用ServeMux的ServeHTTP
26  handler.ServeHTTP(rw, req)
27}
28
29// ServeHTTP
30func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
31  ...
32  h, _ := mux.Handler(r)
33  h.ServeHTTP(w, r)
34}
35
36// Handler
37func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
38  ...
39  return mux.handler(host, r.URL.Path)
40}
41
42// handler
43func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
44  ...
45  if mux.hosts {
46    h, pattern = mux.match(host + path)
47  }
48  if h == nil {
49    h, pattern = mux.match(path)
50  }
51  if h == nil {
52    h, pattern = NotFoundHandler(), ""
53  }
54  return
55}
56
57// 匹配路由函数
58func (mux *ServeMux) match(path string) (h Handler, pattern string) {
59  // 先从前面介绍的ServeMux的m中精确查找路由表达式
60  v, ok := mux.m[path]
61  // 如果找到,直接返回handler
62  if ok {
63    return v.h, v.pattern
64  }
65
66  // 如果不能精确匹配,就去列表中找到最接近的路由
67  // mux.es中的路由是按照从长到短排序的
68  for _, e := range mux.es {
69    if strings.HasPrefix(path, e.pattern) {
70      return e.h, e.pattern
71    }
72  }
73  return nil, ""
74}