• 请不要在回答技术问题时复制粘贴 AI 生成的内容
lauix
V2EX  ›  程序员

GO 如何实现 页面非阻塞?

  •  
  •   lauix · Jun 20, 2018 · 9658 views
    This topic created in 3066 days ago, the information mentioned may be changed or developed.
    如题

    页面非阻塞,就是一个页面访问要 5 秒,10 人访问不排队,并发进行。

    Nodejs 自带,Python tornado gevent 都可以实现。

    今天研究 golang 发现找不到如何实现。。。

    研究了 channel 还是不行

    问问 v2 的大神们,有解决方法吗? or 是否有 go web 的高性能解决方案。

    基于 web 框架 iris 开发研究测试。
    Supplement 1  ·  Jun 20, 2018
    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 再执行,不是并发执行)

    Supplement 2  ·  Jun 21, 2018
    不一一回复了。
    ZSeptember 大牛给了答案,浏览器的问题。
    Chrome 同时打开会阻塞,我使用 Safari 和 Chrome 就是非阻塞了。
    Safari 同时打开也是非阻塞,就是 Chrome 浏览器的问题 。
    为什么出出现这种问题? http://www.laruence.com/2011/07/16/2123.html
    66 replies  •  2018-06-21 15:30:10 +08:00
    dishonest
    dishonest
    Jun 20, 2018
    1
    每个请求 go 不是会开一个 goroutine 吗,这就是并发的呀
    gouchaoer
    gouchaoer
    Jun 20, 2018
    2
    你用 go 写的都是非阻塞的
    k9982874
    k9982874
    Jun 20, 2018
    3
    GOMAXPROCS 了解一下?
    feiyuanqiu
    feiyuanqiu
    Jun 20, 2018❤️1
    4
    可以用 buffered channel,也可以用 select{ default: } 做 Non-Blocking Channel 操作

    https://tour.golang.org/concurrency/6
    lauix
    lauix
    OP
    Jun 20, 2018
    5
    @dishonest
    @gouchaoer
    没有啊,(代码里写了个 10 秒等待) 两个浏览器同时访问一个页面,都是依次排队执行的。第一个浏览器执行完,第二个浏览器才会执行。
    lauix
    lauix
    OP
    Jun 20, 2018
    6
    @k9982874
    这个我知道,并不能解决 页面非阻塞 也就是 event loop

    @feiyuanqiu
    看了看,一样不能实现 页面非阻塞
    nazor
    nazor
    Jun 20, 2018 via iPhone
    7
    其实我更好奇你写出的阻塞代码。
    looplj
    looplj
    Jun 20, 2018
    8
    我也挺好奇的。贴出你的代码看看。
    LT
    LT
    Jun 20, 2018
    9
    其实我更好奇你写出的阻塞代码。每个 http 请求本来就是独立的啊
    mritd
    mritd
    Jun 20, 2018
    10
    Talk is cheap, show me the code!
    alexsunxl
    alexsunxl
    Jun 20, 2018
    11
    非常明确, 是你的代码有问题.. 贴出来吧
    lauix
    lauix
    OP
    Jun 20, 2018
    12
    @nazor
    @ZSeptember
    @LT
    @alexsunxl
    @mritd

    看 追加的内容
    LT
    LT
    Jun 20, 2018
    13
    time.sleep 是主线程休眠,你要模拟延迟响应应该写盗 go func(){}里面
    LT
    LT
    Jun 20, 2018
    14
    ```
    func test(ctx context.Context) {
    go func(){
    fmt.Println("start")

    // 等待 10 秒
    time.Sleep(time.Duration(10) * time.Second)

    fmt.Println("end")

    ctx.Text("test")
    }()
    }

    ```
    flyingnn
    flyingnn
    Jun 20, 2018
    15
    是并发的啊,看控制台输出:
    start
    start
    end
    end
    mkeith
    mkeith
    Jun 20, 2018❤️1
    17
    @flyingnn 你和楼主分别是什么操作系统啊?
    tysx
    tysx
    Jun 20, 2018
    18
    mark 一下,好奇你是怎么做到的
    march1993
    march1993
    Jun 20, 2018❤️2
    19
    你看看右下角图的 URL 是什么鬼……
    dishonest
    dishonest
    Jun 20, 2018
    20
    @LT time.Sleep 是 pause current goroutine。

    iris 没用过,或许是 iris 的问题?
    flyingnn
    flyingnn
    Jun 20, 2018
    21
    @mkeith 我的是 WIN10:Microsoft Windows [版本 10.0.16299.431]
    dishonest
    dishonest
    Jun 20, 2018
    22
    @march1993 哈。。。
    march1993
    march1993
    Jun 20, 2018❤️1
    23
    @dishonest 右下角的 chrome 的 URL 是错的……
    LT
    LT
    Jun 20, 2018
    24
    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")
    }
    LT
    LT
    Jun 20, 2018
    25
    @dishonest 看这个 issue 解释 kataras/iris/issues/463 然后,测试下 上一条回复的代码
    march1993
    march1993
    Jun 20, 2018
    26
    @dishonest 啊 我 sb 了…没问题
    lauix
    lauix
    OP
    Jun 20, 2018
    27
    @LT 用 go func 那是异步了,我想要返回异步里的内容 怎么办?

    @flyingnn 你为什么可以?
    @march1993 浏览器的推荐,URL 是对的
    @dishonest 应该和框架没关系吧,我有空去试试 gin

    @LT 看了你第二个回复,这样执行没有问题,可以取到数据,但是还是阻塞的。我都试过了,没有办法才来 V2 发帖了。
    LT
    LT
    Jun 20, 2018
    28
    没有阻塞额。。。直接在 go func 外层定义变量, 里层接收值就可以了,
    lauix
    lauix
    OP
    Jun 20, 2018
    29
    @LT 我复制你的代码是阻塞的,我的系统 MAC 应该和系统没有关系。你怎么调试的?
    myyou
    myyou
    Jun 20, 2018
    30
    我用 gin 框架,和楼主一样方式测试,没有楼主这个问题

    ![code]( )
    ![log]( )

    是不是 iris 框架有问题?
    janxin
    janxin
    Jun 20, 2018
    31
    破案了,lz 你第二个浏览器网址打错了
    aisk
    aisk
    Jun 20, 2018
    32
    是不是用的比较老的 GO 版本?
    AlphaTr
    AlphaTr
    Jun 20, 2018
    33
    测试了 gen,echo 和 iris 三个框架,都复现了楼主的问题,go version go1.10.2 darwin/amd64

    iris:





    echo:





    gen:



    looplj
    looplj
    Jun 20, 2018❤️5
    34
    别用浏览器,用 curl 就不会这样了。
    gamexg
    gamexg
    Jun 20, 2018
    36
    可以复现,甚至标准库 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
    AlphaTr
    AlphaTr
    Jun 20, 2018
    37
    我去,楼上破案了,坑了
    gamexg
    gamexg
    Jun 20, 2018
    38
    @ZSeptember #34 测试后的确时浏览器的问题,procexp 显示只有 1 个连接...
    yanhejihe
    yanhejihe
    Jun 20, 2018
    39
    这真是奇怪,目前不清楚原因,等破案。
    nazor
    nazor
    Jun 20, 2018 via iPhone
    40
    用不同浏览器同时请求,出现这个问题可能是因为 goroutine 是针对 tcp 连接的
    wei193
    wei193
    Jun 20, 2018
    41
    13 楼上不是说了吗? time.Sleep 的锅
    yanhejihe
    yanhejihe
    Jun 20, 2018
    42
    哇,在我测试 demo 过程就破案了,应该就是浏览器的问题。
    wei193
    wei193
    Jun 20, 2018
    43
    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
    wei193
    wei193
    Jun 20, 2018
    44
    @wei193 回复 43 楼 测试有误, 应该是 goroutine 针对连接的 因为将上面的代码修改 time.Sleep 一样是并发
    elvodn
    elvodn
    Jun 20, 2018
    45
    ``` 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 没问题啊, 浏览器内多个请求是阻塞的
    icexin
    icexin
    Jun 20, 2018❤️6
    46
    chrome 的锅。打开 console,看下请求时间主要在 stalled 上,由于两次请求是同一个资源,chrome 加了一把锁,导致同步请求。如果把 console 里面的 disable cache 勾上就没事了。
    见这篇文章 http://fex.baidu.com/blog/2015/01/chrome-stalled-problem-resolving-process/
    Shakeitin
    Shakeitin
    Jun 20, 2018
    47

    在 34 楼之后居然能再续十楼,惊了
    ps: 两边时钟不准,勿细究
    pathbox
    pathbox
    Jun 20, 2018 via iPhone
    48
    @icexin 可以理解为同一个 Chrome 访问同一个资源加锁吗?不同的 Chrome 是不会加锁的吧?
    icexin
    icexin
    Jun 20, 2018
    49
    看着是这样的,我一个开隐身,一个不开就没问题
    iceheart
    iceheart
    Jun 20, 2018 via Android
    50
    pipeline
    scnace
    scnace
    Jun 20, 2018 via Android
    51
    你们真的不是写 go test 测试 但是去用浏览器试这个问题的吗 hhh
    rrfeng
    rrfeng
    Jun 20, 2018
    52
    果然是浏览器的问题,跟我预想的差不多。

    Chrome 总有些奇怪的骚操作
    Reficul
    Reficul
    Jun 20, 2018❤️1
    53
    Go 的 http 服务器本来就是一个请求一个 goroute 的。想写出阻塞的反而有点难度。
    fan123199
    fan123199
    Jun 20, 2018
    54
    @Reficul 我关注这个问题,就是我前端时间需要用阻塞来处理一个问题。 后来用了 mutex lock 来解决。想不到是个 chrome 还有这种问题。还有一个现象,如果你在 chrome 打开这个网页,然后点 F5 刷新,就不会阻塞。
    karllynn
    karllynn
    Jun 20, 2018
    55
    楼主这个问题看得我一愣一愣的
    ToT
    ToT
    Jun 20, 2018
    56
    @ZSeptember 非常有用的链接,居然是第一次知道这个大牛。
    RubyJack
    RubyJack
    Jun 20, 2018
    57
    1. time.Sleep 当然不是主线程阻塞
    2. 比起锁,感觉像是 http 持久连接带来的问题
    heimeil
    heimeil
    Jun 20, 2018
    58
    同时请求两个一样的 URL,Chrome 会等待第一个请求返回头部信息里的缓存控制规则,第二次请求会再带上缓存规则(如果有的话)在请求头里面,这是 Chrome 的缓存控制优化机制,节省服务器资源,而两个不同的 URL 同时请求就没这种问题,比如 /foo 和 /bar。

    可以了解一下 HTTP 缓存:
    https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn
    lazyago
    lazyago
    Jun 20, 2018
    59
    如果是浏览器问题,那为什么题主使用 tornado 却没有复现此问题?
    auin
    auin
    Jun 21, 2018
    60
    厉害了,我第一次发现浏览器会串化相同请求。 👍
    lauix
    lauix
    OP
    Jun 21, 2018
    62
    @ZSeptember 确实是浏览器的问题,感谢!
    CloudnuY
    CloudnuY
    Jun 21, 2018
    63
    亏我以前抢购还开好几个 tab ……
    fcten
    fcten
    Jun 21, 2018
    64
    这个策略是很正常的。因为任何一个 GET 请求都是有可能被缓存的,所以并发执行 GET 往往是不必要的。一旦第一个 GET 请求返回并且允许缓存,后续 GET 请求都不必再执行。
    这个策略主要是为了优化静态资源的加载。
    dishonest
    dishonest
    Jun 21, 2018
    65
    学习了~
    xiadada
    xiadada
    Jun 21, 2018
    66
    我用原生的 go http 测试了一下, get 同一个地址,确实是串行化的. 这是 Chrome 的问题. 我猜想没有一个 go server 框架在处理 request 的时候回串行处理. 所以请不要在 handler 方法里写什么 go func(){} 还有人不控制 go 的退出结果,后台裸跑 go,,更傻)

    解决的办法很简单, get 地址可以变一变嘛, 请求里塞一个时间戳 /12312 /546 都看成同一个东西就好了.
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   906 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 92ms · UTC 21:18 · PVG 05:18 · LAX 13:18 · JFK 16:18
    ♥ Do have faith in what you're doing.