package main import ( "github.com/kataras/iris" "github.com/kataras/iris/context" "fmt" "time" ) func test(ctx context.Context) { fmt.Println("start") // 等待 10 秒 time.Sleep(time.Duration(10) * time.Second) fmt.Println("end") ctx.Text("test") } func app_init() *iris.Application { // 创建应用 app := iris.New() // 开启 debug app.Logger().SetLevel("debug") // 路由初始化 app.Get("/", test) return app } func main() { //应用初始化 app := app_init() //启动 WEB app.Run(iris.Addr(":8080")) }
DEMO 测试 ( 1 先执行的,然后 2 再执行,不是并发执行)
![]() | 1 dishonest 2018-06-20 16:31:03 +08:00 每个请求 go 不是会开一个 goroutine 吗,这就是并发的呀 |
![]() | 2 gouchaoer 2018-06-20 16:31:58 +08:00 你用 go 写的都是非阻塞的 |
![]() | 3 k9982874 2018-06-20 16:34:12 +08:00 GOMAXPROCS 了解一下? |
4 feiyuanqiu 2018-06-20 16:34:30 +08:00 ![]() 可以用 buffered channel,也可以用 select{ default: } 做 Non-Blocking Channel 操作 https://tour.golang.org/concurrency/6 |
![]() | 5 lauix OP |
![]() | 6 lauix OP |
![]() | 7 nazor 2018-06-20 16:44:04 +08:00 via iPhone 其实我更好奇你写出的阻塞代码。 |
![]() | 8 looplj 2018-06-20 16:47:05 +08:00 我也挺好奇的。贴出你的代码看看。 |
9 LT 2018-06-20 16:48:45 +08:00 其实我更好奇你写出的阻塞代码。每个 http 请求本来就是独立的啊 |
![]() | 10 mritd 2018-06-20 16:52:45 +08:00 Talk is cheap, show me the code! |
11 alexsunxl 2018-06-20 16:58:53 +08:00 非常明确, 是你的代码有问题.. 贴出来吧 |
13 LT 2018-06-20 17:21:08 +08:00 time.sleep 是主线程休眠,你要模拟延迟响应应该写盗 go func(){}里面 |
14 LT 2018-06-20 17:21:30 +08:00 ``` func test(ctx context.Context) { go func(){ fmt.Println("start") // 等待 10 秒 time.Sleep(time.Duration(10) * time.Second) fmt.Println("end") ctx.Text("test") }() } ``` |
![]() | 15 flyingnn 2018-06-20 17:26:17 +08:00 是并发的啊,看控制台输出: start start end end |
16 LT 2018-06-20 17:30:43 +08:00 |
![]() | 18 tysx 2018-06-20 17:32:33 +08:00 mark 一下,好奇你是怎么做到的 |
19 march1993 2018-06-20 17:33:11 +08:00 ![]() 你看看右下角图的 URL 是什么鬼…… |
![]() | 20 dishonest 2018-06-20 17:33:35 +08:00 |
24 LT 2018-06-20 17:36:32 +08:00 func test(ctx context.Context) { c := make(chan int) fmt.Println("start") go func(){ // 等待 10 秒 time.Sleep(time.Duration(5) * time.Second) c <- 0 }() <- c fmt.Println("end") ctx.Text("test") } |
![]() | 27 lauix OP |
28 LT 2018-06-20 17:45:54 +08:00 没有阻塞额。。。直接在 go func 外层定义变量, 里层接收值就可以了, |
![]() | 30 myyou 2018-06-20 18:07:40 +08:00 我用 gin 框架,和楼主一样方式测试,没有楼主这个问题   是不是 iris 框架有问题? |
![]() | 31 janxin 2018-06-20 18:17:10 +08:00 破案了,lz 你第二个浏览器网址打错了 |
![]() | 32 aisk 2018-06-20 18:25:41 +08:00 是不是用的比较老的 GO 版本? |
![]() | 33 AlphaTr 2018-06-20 18:34:09 +08:00 测试了 gen,echo 和 iris 三个框架,都复现了楼主的问题,go version go1.10.2 darwin/amd64 iris: ![]() ![]() echo: ![]() ![]() gen: ![]() ![]() |
![]() | 34 looplj 2018-06-20 18:41:01 +08:00 ![]() 别用浏览器,用 curl 就不会这样了。 |
![]() | 35 looplj 2018-06-20 18:42:07 +08:00 ![]() |
![]() | 36 gamexg 2018-06-20 18:44:37 +08:00 可以复现,甚至标准库 http 实现也可以复现。 ``` package main import ( "fmt" "net/http" "time" ) func handler(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "text/plain") fmt.Println("start") time.Sleep(10 * time.Second) fmt.Println("end") } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8088", nil) } ``` 同时打开 3 个页面输出: start end start start end start end start end start end end |
![]() | 37 AlphaTr 2018-06-20 18:44:39 +08:00 我去,楼上破案了,坑了 |
![]() | 38 gamexg 2018-06-20 18:47:52 +08:00 @ZSeptember #34 测试后的确时浏览器的问题,procexp 显示只有 1 个连接... |
39 yanhejihe 2018-06-20 19:03:44 +08:00 这真是奇怪,目前不清楚原因,等破案。 |
![]() | 40 nazor 2018-06-20 19:04:42 +08:00 via iPhone 用不同浏览器同时请求,出现这个问题可能是因为 goroutine 是针对 tcp 连接的 |
41 wei193 2018-06-20 19:05:47 +08:00 13 楼上不是说了吗? time.Sleep 的锅 |
42 yanhejihe 2018-06-20 19:07:12 +08:00 哇,在我测试 demo 过程就破案了,应该就是浏览器的问题。 |
43 wei193 2018-06-20 19:11:50 +08:00 switch r.URL.Path { case "/": log.Println(1) s.Lock() // time.Sleep log.Println(2) fmt.Fprintf(w, "hello word: %d\n", time.Now().Unix()) default: log.Println(1) s.Unlock() // time.Sleep log.Println(2) fmt.Fprintf(w, "hello word: %d\n", time.Now().Unix()) } 以上代码可以实现并发 所以我觉得是 time.Sleep 问题,测试环境 go version go1.9 darwin/amd64 |
44 wei193 2018-06-20 19:16:02 +08:00 @wei193 回复 43 楼 测试有误, 应该是 goroutine 针对连接的 因为将上面的代码修改 time.Sleep 一样是并发 |
45 elvodn 2018-06-20 19:19:42 +08:00 ``` go package main import ( "fmt" "net/http" "time" ) func handler(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "text/plain") fmt.Printf("[%v] start %v %v \n", time.Now(), req.RemoteAddr, req.URL) time.Sleep(10 * time.Second) fmt.Printf("[%v] end %v %v\n", time.Now(), req.RemoteAddr, req.URL) } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8088", nil) } ``` 1.10.3 没问题啊, 浏览器内多个请求是阻塞的 |
46 icexin 2018-06-20 19:41:43 +08:00 ![]() chrome 的锅。打开 console,看下请求时间主要在 stalled 上,由于两次请求是同一个资源,chrome 加了一把锁,导致同步请求。如果把 console 里面的 disable cache 勾上就没事了。 见这篇文章 http://fex.baidu.com/blog/2015/01/chrome-stalled-problem-resolving-process/ |
47 Shakeitin 2018-06-20 19:44:59 +08:00 ![]() ![]() 在 34 楼之后居然能再续十楼,惊了 ps: 两边时钟不准,勿细究 |
![]() | 48 pathbox 2018-06-20 20:01:03 +08:00 via iPhone @icexin 可以理解为同一个 Chrome 访问同一个资源加锁吗?不同的 Chrome 是不会加锁的吧? |
49 icexin 2018-06-20 20:15:52 +08:00 看着是这样的,我一个开隐身,一个不开就没问题 |
50 iceheart 2018-06-20 20:30:23 +08:00 via Android pipeline |
51 scnace 2018-06-20 20:48:46 +08:00 via Android 你们真的不是写 go test 测试 但是去用浏览器试这个问题的吗 hhh |
![]() | 52 rrfeng 2018-06-20 20:57:07 +08:00 果然是浏览器的问题,跟我预想的差不多。 Chrome 总有些奇怪的骚操作 |
![]() | 53 Reficul 2018-06-20 21:07:54 +08:00 ![]() Go 的 http 服务器本来就是一个请求一个 goroute 的。想写出阻塞的反而有点难度。 |
54 fan123199 2018-06-20 21:44:41 +08:00 @Reficul 我关注这个问题,就是我前端时间需要用阻塞来处理一个问题。 后来用了 mutex lock 来解决。想不到是个 chrome 还有这种问题。还有一个现象,如果你在 chrome打开这个网页,然后点 F5 刷新,就不会阻塞。 |
55 karllynn 2018-06-20 21:46:10 +08:00 楼主这个问题看得我一愣一愣的 |
56 ToT 2018-06-20 21:55:00 +08:00 @ZSeptember 非常有用的链接,居然是第一次知道这个大牛。 |
![]() | 57 RubyJack 2018-06-20 22:20:40 +08:00 1. time.Sleep 当然不是主线程阻塞 2. 比起锁,感觉像是 http 持久连接带来的问题 |
58 heimeil 2018-06-20 22:43:54 +08:00 同时请求两个一样的 URL,Chrome 会等待第一个请求返回头部信息里的缓存控制规则,第二次请求会再带上缓存规则(如果有的话)在请求头里面,这是 Chrome 的缓存控制优化机制,节省服务器资源,而两个不同的 URL 同时请求就没这种问题,比如 /foo 和 /bar。 可以了解一下 HTTP 缓存: https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn |
![]() | 59 lazyago 2018-06-20 23:24:31 +08:00 如果是浏览器问题,那为什么题主使用 tornado 却没有复现此问题? |
![]() | 60 liuxey 2018-06-21 09:14:11 +08:00 厉害了,我第一次发现浏览器会串化相同请求。 |
![]() | 62 lauix OP @ZSeptember 确实是浏览器的问题,感谢! |
![]() | 63 CloudnuY 2018-06-21 10:53:16 +08:00 亏我以前抢购还开好几个 tab …… |
![]() | 64 fcten 2018-06-21 11:03:42 +08:00 这个策略是很正常的。因为任何一个 GET 请求都是有可能被缓存的,所以并发执行 GET 往往是不必要的。一旦第一个 GET 请求返回并且允许缓存,后续 GET 请求都不必再执行。 这个策略主要是为了优化静态资源的加载。 |
![]() | 65 dishonest 2018-06-21 11:23:57 +08:00 学习了~ |
66 xiadada 2018-06-21 15:30:10 +08:00 我用原生的 go http 测试了一下, get 同一个地址,确实是串行化的. 这是 Chrome 的问题. 我猜想没有一个 go server 框架在处理 request 的时候回串行处理. 所以请不要在 handler 方法里写什么 go func(){} 还有人不控制 go 的退出结果,后台裸跑 go,,更傻) 解决的办法很简单, get 地址可以变一变嘛, 请求里塞一个时间戳 /12312 /546 都看成同一个东西就好了. |