ListenAndServe()

Screen Shot 2019-09-14 at 9.33.39 PM.png

TLS 需要在创建的时候传入证书和密钥,然后通过这两个创建 TLSListener ,普通的只需要 addr 和 handler (如果 handler 为空,会自动使用 DefaultServeMux)
tcpKeepAliveListener 只是将 TCP 的连接设置为 KeepAlive 的并且时间为 10 minute
onceCloseListener 是为了防止 Listener 多次关闭,所以又进行了封装
经过测试,一个 Server 可以监听多个 Listener 这些 Listener 都会被追踪,并且存储在 map 结构中

Server.Serve()

服务器的 Serve() 主体是一个 for 循环,这里主要是通过 Listener 获取 conn,然后使用自身的 conn 进行封装,然后进入 goroutine 进行服务

  1. for {
  2. rw, e := l.Accept()
  3. if e != nil {
  4. select {
  5. case <-srv.getDoneChan():
  6. return ErrServerClosed
  7. default:
  8. }
  9. if ne, ok := e.(net.Error); ok && ne.Temporary() {
  10. if tempDelay == 0 {
  11. tempDelay = 5 * time.c
  12. } else {
  13. tempDelay *= 2
  14. }
  15. if max := 1 * time.Second; tempDelay > max {
  16. tempDelay = max
  17. }
  18. srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
  19. time.Sleep(tempDelay)
  20. continue
  21. }
  22. return e
  23. }
  24. tempDelay = 0
  25. c := srv.newConn(rw)
  26. c.setState(c.rwc, StateNew) // before Serve can return
  27. go c.serve(ctx)
  28. }

其中有一段错误处理,用于处理建立连接时的网络拥塞

1566993000331-47353a47-04e9-46eb-9350-53f075df606a.png

conn.Serve()

Screen Shot 2019-09-15 at 10.32.45 AM.png

经过 defer recover 还有 tls 建立连接之后,就真正进入了处理请求的环节,
系统会根据注册的路径去分配不同的 handler,这个连接可以被复用

servemux.png

这里有两种匹配模式,一种是精确匹配

  1. // Check for exact match first.
  2. v, ok := mux.m[path]
  3. if ok {
  4. return v.h, v.pattern

还有一种是模糊匹配

  1. // Check for longest valid match. mux.es contains all patterns
  2. // that end in / sorted from longest to shortest.
  3. for _, e := range mux.es {
  4. if strings.HasPrefix(path, e.pattern) {
  5. return e.h, e.pattern
  6. }
  7. }

Write

conn 的所有数据都是从 net.Conn 中读取出来的,但是通过这个连接,分成了 bufio.Reader 和 bufio.Writer 分别用于读写,它们给 conn 附加了缓存的功能,提高了进行读写的效率。
另外所有的 Writer 都是通过 bufioWriterPool 来进行获取的,通过这个池子,在一定程度上限定了连接的数量

  1. size := runtime.GOMAXPROCS(0)
  2. local := make([]poolLocal, size)

在 sync 中池子中的底层数组的大小是与 P 的最大数目相关的,通过这个结构可以提高并发的效率,加大并发数。

Untitled Diagram.png

在生成 conn 开始进入服务状态的时候,会先生成 bufio.Readerbufio.Writer 但是好像在每次 readReques() 函数中,又会为每一个 Response 生成一个 bufio.Writer ,并不会使用前面已经分配过的

而且在 reader 还包装了一层 textProtoReader 用于将文本解析为结构体,同样 reader 外也有 chunkWrite 专门进行 Header 的编码。

Cancle

每一次请求应该都有超时机制,防止资源继续浪费下去。

Untitled Diagram.png

首先是读取请求时的超时设置,这三个函数都是在 readRequest() 中被执行的,但是他们的时间不同,第一个是最先执行的,这时还完全没有进行读取,但是有时候因为没有对 readHeadTimeout 进行设置,可能会使用 ReadTimeout 所以在读取完头部之后,还会对这两个 Deadline 进行对比,然后决定是否执行第二个函数,最后一个是在 readRequest() 结束的时候,这时基本上可以认为已经进入数据处理阶段了,所以可以进行写入的超时设置了。