ListenAndServe()
TLS 需要在创建的时候传入证书和密钥,然后通过这两个创建 TLSListener ,普通的只需要 addr 和 handler (如果 handler 为空,会自动使用 DefaultServeMux)tcpKeepAliveListener
只是将 TCP 的连接设置为 KeepAlive 的并且时间为 10 minuteonceCloseListener
是为了防止 Listener 多次关闭,所以又进行了封装
经过测试,一个 Server 可以监听多个 Listener 这些 Listener 都会被追踪,并且存储在 map 结构中
Server.Serve()
服务器的 Serve()
主体是一个 for
循环,这里主要是通过 Listener 获取 conn,然后使用自身的 conn 进行封装,然后进入 goroutine 进行服务
for {
rw, e := l.Accept()
if e != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.c
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
其中有一段错误处理,用于处理建立连接时的网络拥塞
conn.Serve()
经过 defer recover 还有 tls 建立连接之后,就真正进入了处理请求的环节,
系统会根据注册的路径去分配不同的 handler,这个连接可以被复用
这里有两种匹配模式,一种是精确匹配
// Check for exact match first.
v, ok := mux.m[path]
if ok {
return v.h, v.pattern
」
还有一种是模糊匹配
// Check for longest valid match. mux.es contains all patterns
// that end in / sorted from longest to shortest.
for _, e := range mux.es {
if strings.HasPrefix(path, e.pattern) {
return e.h, e.pattern
}
}
Write
conn 的所有数据都是从 net.Conn 中读取出来的,但是通过这个连接,分成了 bufio.Reader 和 bufio.Writer 分别用于读写,它们给 conn 附加了缓存的功能,提高了进行读写的效率。
另外所有的 Writer 都是通过 bufioWriterPool 来进行获取的,通过这个池子,在一定程度上限定了连接的数量
size := runtime.GOMAXPROCS(0)
local := make([]poolLocal, size)
在 sync 中池子中的底层数组的大小是与 P 的最大数目相关的,通过这个结构可以提高并发的效率,加大并发数。
在生成 conn 开始进入服务状态的时候,会先生成 bufio.Reader
和 bufio.Writer
但是好像在每次 readReques()
函数中,又会为每一个 Response 生成一个 bufio.Writer
,并不会使用前面已经分配过的
而且在 reader 还包装了一层 textProtoReader 用于将文本解析为结构体,同样 reader 外也有 chunkWrite 专门进行 Header 的编码。
Cancle
每一次请求应该都有超时机制,防止资源继续浪费下去。
首先是读取请求时的超时设置,这三个函数都是在 readRequest() 中被执行的,但是他们的时间不同,第一个是最先执行的,这时还完全没有进行读取,但是有时候因为没有对 readHeadTimeout 进行设置,可能会使用 ReadTimeout 所以在读取完头部之后,还会对这两个 Deadline 进行对比,然后决定是否执行第二个函数,最后一个是在 readRequest() 结束的时候,这时基本上可以认为已经进入数据处理阶段了,所以可以进行写入的超时设置了。