net/http的server端的源码学习

先创建一个简单的demo,执行后的调用链路在这里

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello World"))
    })

    if err := http.ListenAndServe(":8000", nil); err != nil {
        fmt.Println("start http server fail:", err)
    }
}

我们知道用c语言实现一个tcp服务,用到的系统调用有socket,bind,listen,我们看看在go中是如何做的。
go编译运行后,程序会阻塞监听在8080端口上,这个阶段的调用链路如下:

|-goroutine-1 created by runtime.main
  |-main.main
    |-net/http.(*ServeMux).Handle       // 路由注册到net/http.DefaultServeMux的map中
      |-net/http.appendSorted
    |-net/http.(*Server).ListenAndServe
      |-net.Listen
        |-net.(*ListenConfig).Listen
          |-net.(*sysListener).listenTCP
            |-net.internetSocket
              |-net.socket
                |-net.sysSocket
                  |-runtime/internal/syscall.Syscall6 // 系统调用socket
                |-net.setDefaultSockopts
                |-net.listenerBacklog
                |-net.(*netFD).listenStream
                  |-net.setDefaultListenerSockopts
                  |-net.(*TCPAddr).sockaddr
                  |-syscall.Bind                      // 系统调用bind
                  |-listenFunc(fd.pfd.Sysfd, backlog) // 系统调用listen
                  |-internal/poll.(*FD).Init
                  |-net.sockaddrToTCP
      |-net/http.(*Server).Serve
        |-net/http.(*onceCloseListener).Accept
          |-net.(*TCPListener).Accept
            |-net.(*TCPListener).accept
              |-net.(*netFD).accept
                |-internal/poll.(*FD).Accept
                  |-internal/poll.(*pollDesc).wait
                    |-internal/poll.runtime_pollWait
                      |-netpollblock                 // 协程挂起,等有新连接了,netpoll模型唤醒它
        |-net/http.(*Server).Serve.func3             // 新连接用新协程来服务它
          |-go c.serve(connCtx)

模拟请求一下8000端口,看看新协程的服务流程:

|-goroutine-18 created by net/http.(*Server).Serve.func3
  |-net/http.(*conn).serve
    |-net/http.(*conn).readRequest       // 读取请求
    |-net/http.serverHandler.ServeHTTP   // 这里如果没有serverMux,则用net/http.DefaultServeMux
      |-net/http.(*ServeMux).ServeHTTP
        |-net/http.(*ServeMux).Handler
          |-net/http.(*ServeMux).handler
            |-net/http.(*ServeMux).match // 这里通过请求的path匹配对应的handle(全字匹配/前缀匹配)
        |-net/http.HandlerFunc.ServeHTTP 
          |-main.main.func1              // handle方法就是demo代码里面定义的
    |-net/http.(*response).finishRequest // 返回响应内容

至此大概的流程分析完成,可以看到官方的net包创建的server服务有几个问题:

  1. 每个新连接都会启动一个新协程,连接关闭后协程也销毁了,不能复用
  2. 路由匹配逻辑过于简单,也不高效
  3. 路由没有区分请求类型,如GET/POST
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!