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 再执行,不是并发执行)

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