draveness / blog-comments

面向信仰编程
https://draveness.me
140 stars 6 forks source link

Go 语言 HTTP 标准库的实现原理 | Go 语言设计与实现 · /golang-net-http #207

Closed draveness closed 2 years ago

draveness commented 4 years ago

https://draveness.me/golang/docs/part4-advanced/ch09-stdlib/golang-net-http/

Go 语言 HTTP 标准库的实现原理

yjhmelody commented 4 years ago

练级 => 连接


2020-05-19 UPDATES: 已修复

ansionfor commented 4 years ago

图片加载不出来了

tpkeeper commented 4 years ago

一直好奇 gin 用 trie 实现的路由跟用 map 实现的有啥区别,原来官方的就是用的map啊

Ehco1996 commented 4 years ago

@tpkeeper 一直好奇 gin 用 trie 实现的路由跟用 map 实现的有啥区别,原来官方的就是用的map啊

map是键值对的存储的方式,只能用来索引静态路由,并不支持[path_param] 而前缀树就可以

举个例子: api/users/:name 这样的动态路由 可以匹配

draveness commented 4 years ago

图片加载不出来了

应该是没有问题的,你在看看

yuanjize commented 4 years ago

今天看到服务有协程泄露了,然后用pprof看到好多卡在了getConn的select语句,猜测是因为我们配置的一些transport的与连接池(keep-alive)相关参数不合理导致的。然后所有调用改成了使用http.DefaultClient.Do就好了。。。。

caofengl commented 4 years ago
package main

import (
    "fmt"
    "net/http"
    "time"
)

type f struct {
}

func main() {
    server := &http.Server{
        ReadTimeout:  1 * time.Second,
        WriteTimeout: 2 * time.Second,
        Handler:      f{},
        Addr:         ":8099",
    }
    server.ListenAndServe()
}

func (f) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    time.Sleep(5 * time.Second)
    fmt.Println("5s")
    time.Sleep(2 * time.Second)
    fmt.Println("7s")
    fmt.Fprintf(w, "hello\n")
}

请教博主一个问题,在 http.Server 中设置 WriteTimeout 为 2s,然后客户端请求这个接口,预期是客户端在 2s 多一些收到 FIN。但实际测试发现服务端是执行完 ServeHTTP 的逻辑才发送 FIN 。抓包结果如下。对 go 中的 ContextCancel 是如何实现的,取消一个 goroutine 的流程有疑惑。还望博主和各位 gopher 不吝解答,感谢🙏。

16:47:01.476863 IP6 localhost.50172 > localhost.8099: Flags [S], seq 3351420287, win 65535, options [mss 16324,nop,wscale 6,nop,nop,TS val 1149380157 ecr 0,sackOK,eol], length 0
16:47:01.476958 IP6 localhost.8099 > localhost.50172: Flags [S.], seq 2608306878, ack 3351420288, win 65535, options [mss 16324,nop,wscale 6,nop,nop,TS val 1149380157 ecr 1149380157,sackOK,eol], length 0
16:47:01.476971 IP6 localhost.50172 > localhost.8099: Flags [.], ack 1, win 6371, options [nop,nop,TS val 1149380157 ecr 1149380157], length 0
16:47:01.476982 IP6 localhost.8099 > localhost.50172: Flags [.], ack 1, win 6371, options [nop,nop,TS val 1149380157 ecr 1149380157], length 0
16:47:01.477116 IP6 localhost.50172 > localhost.8099: Flags [P.], seq 1:194, ack 1, win 6371, options [nop,nop,TS val 1149380157 ecr 1149380157], length 193
16:47:01.477132 IP6 localhost.8099 > localhost.50172: Flags [.], ack 194, win 6368, options [nop,nop,TS val 1149380157 ecr 1149380157], length 0
16:47:08.483058 IP6 localhost.8099 > localhost.50172: Flags [F.], seq 1, ack 194, win 6368, options [nop,nop,TS val 1149387127 ecr 1149380157], length 0
16:47:08.483086 IP6 localhost.50172 > localhost.8099: Flags [.], ack 2, win 6371, options [nop,nop,TS val 1149387127 ecr 1149387127], length 0
16:47:08.501720 IP6 localhost.50172 > localhost.8099: Flags [F.], seq 194, ack 2, win 6371, options [nop,nop,TS val 1149387145 ecr 1149387127], length 0
16:47:08.501766 IP6 localhost.8099 > localhost.50172: Flags [.], ack 195, win 6368, options [nop,nop,TS val 1149387145 ecr 1149387145], length 0
draveness commented 4 years ago

请教博主一个问题,在 http.Server 中设置 WriteTimeout 为 2s,然后客户端请求这个接口,预期是客户端在 2s 多一些收到 FIN。但实际测试发现服务端是执行完 ServeHTTP 的逻辑才发送 FIN 。抓包结果如下。对 go 中的 ContextCancel 是如何实现的,取消一个 goroutine 的流程有疑惑。还望博主和各位 gopher 不吝解答,感谢🙏。

这个 Deadline 是直接设置在 FD 上的 https://github.com/golang/go/blob/b8fd3cab3944d5dd5f2a50f3cc131b1048897ee1/src/internal/poll/fd_poll_runtime.go#L164

caofengl commented 4 years ago

@draveness

请教博主一个问题,在 http.Server 中设置 WriteTimeout 为 2s,然后客户端请求这个接口,预期是客户端在 2s 多一些收到 FIN。但实际测试发现服务端是执行完 ServeHTTP 的逻辑才发送 FIN 。抓包结果如下。对 go 中的 ContextCancel 是如何实现的,取消一个 goroutine 的流程有疑惑。还望博主和各位 gopher 不吝解答,感谢🙏。

这个 Deadline 是直接设置在 FD 上的 https://github.com/golang/go/blob/b8fd3cab3944d5dd5f2a50f3cc131b1048897ee1/src/internal/poll/fd_poll_runtime.go#L164

多谢博主,发现有一篇文章讲的很好。 https://tracymacding.gitbooks.io/implementation-of-golang/network/net_socket_timeout.html

hellowoody commented 4 years ago

请教一个问题,在http.Conn.serve方法中,为什么readRequest要放在for循环中?

for {
        w, _ := c.readRequest(ctx)
        serverHandler{c.server}.ServeHTTP(w, w.req)
        w.finishRequest()
        ...
}
draveness commented 4 years ago

请教一个问题,在http.Conn.serve方法中,为什么readRequest要放在for循环中?

for {
      w, _ := c.readRequest(ctx)
      serverHandler{c.server}.ServeHTTP(w, w.req)
      w.finishRequest()
      ...
}

看一下原始方法 https://github.com/golang/go/blob/881d5405402d6e8c54f83eed6216a9ed29778006/src/net/http/server.go#L1766 这里省略了一些实现细节

discipline-yyl commented 4 years ago

“如果当前 HTTP 服务接收到了海量的请求,会在内部创建大量的 HTTP 服务,” 这里是想说:会在内部创建大量的Goroutine 吧


2020-07-12 UPDATES: 已修复

Anthony-Dong commented 4 years ago

@tpkeeper 一直好奇 gin 用 trie 实现的路由跟用 map 实现的有啥区别,原来官方的就是用的map啊

gin用的http-router,可以使用模式匹配等功能,那个http-router确实很好用,自己也可以二次开发

cuishuang commented 3 years ago

@Anthony-Dong

@tpkeeper 一直好奇 gin 用 trie 实现的路由跟用 map 实现的有啥区别,原来官方的就是用的map啊

gin用的http-router,可以使用模式匹配等功能,那个http-router确实很好用,自己也可以二次开发

nsq也用了http-router

bynil commented 3 years ago

如果使用默认客户端(比如 http.Get 方法)要注意这个 DefaultClient 发出的请求是没有超时时间的, 某些场景下会一直 hang 在那。


2020-10-30 UPDATES: 已修复

DustOak commented 3 years ago

额 在 HTTP 持久连接的 net/http.persistConn.writeLoop 方法中等待响应; 这里不是 readLoop吗


2021-01-24 UPDATES: 已修复

kingcanfish commented 2 years ago

创建了服务端的连接之后,标准库中的实现会为每个 HTTP 请求创建单独的 Goroutine 并在其中调用 net/http.Conn.serve 方法

这里应该是针对每个TCP连接创建一个单独 Goroutine 而不是每个HTTP请求创建吧