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 1
可以用 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
ZSeptember 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 2
你看看右下角图的 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 框架,和楼主一样方式测试,没有楼主这个问题
![code]( ) ![log]( ) 是不是 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
ZSeptember 2018-06-20 18:41:01 +08:00 5
别用浏览器,用 curl 就不会这样了。
|
35
ZSeptember 2018-06-20 18:42:07 +08:00 1
|
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 6
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 1
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 都看成同一个东西就好了. |