V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
wunonglin
V2EX  ›  Go 编程语言

websocket 为什么是按顺序执行的?

  •  2
     
  •   wunonglin · 2020-07-10 12:38:30 +08:00 · 4763 次点击
    这是一个创建于 1358 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我用的是 echo 里面的 gorilla websocket 示例

    https://echo.labstack.com/cookbook/websocket

    我原本的后台是 node 写的,node 上没事,但是为什么 go 这边是按顺序执行的而不是同时执行?

    https://i.loli.net/2020/07/10/qcGlzNh25ADPULb.png

    第 1 条附言  ·  2020-07-10 15:09:09 +08:00

    这是用node写的,是可以同时执行的

    https://i.loli.net/2020/07/10/CDYJSQ852yxks43.png

    第 2 条附言  ·  2020-07-10 15:23:10 +08:00
    var conn *websocket.Conn
    
    func (c *Controller) Subscribe(ctx echo.Context) error {
    	upgrader := websocket.Upgrader{
    		CheckOrigin: func(r *http.Request) bool {
    			return true
    		},
    		EnableCompression: true,
    	}
    	ws, err := upgrader.Upgrade(ctx.Response(), ctx.Request(), nil)
    	if err != nil {
    		return err
    	}
    	conn = ws
    
    	go c.readLoop(ctx)
    
    	return nil
    }
    
    func (c *Controller) readLoops(ctx echo.Context) {
    	token := ctx.Request().URL.Query().Get("auth")
    	userData, err := c.userService.GetUserDataFromCache(token)
    	if err != nil {
    		_ = conn.WriteJSON(tool.WebsocketResponse("error", err.Error(), nil))
    	}
    
    	for {
    		body := new(socketRequest)
    		if err := conn.ReadJSON(body); err != nil {
    			_ = conn.WriteJSON(tool.WebsocketResponse("error", err.Error(), nil))
    		}
    		if err := ctx.Validate(body); err != nil {
    			_ = conn.WriteJSON(tool.WebsocketResponse("error", err.Error(), nil))
    		} else {
    			switch body.Event {
    			case "create":
    				albumId, err := c.hashId.HashIdDecode(*body.Payload.AlbumId)
    				if err != nil {
    					_ = conn.WriteJSON(tool.WebsocketResponse("error", err.Error(), nil))
    				}
    
    				if event, err := c.CreateFromUrl(userData.Id, albumId, *body.Payload.Url); err != nil {
    					_ = conn.WriteJSON(tool.WebsocketResponse(event, err.Error(), body.Payload))
    				} else {
    					_ = conn.WriteJSON(tool.WebsocketResponse(event, "", body.Payload))
    				}
    			}
    		}
    	}
    }
    
    第 3 条附言  ·  2020-07-11 01:43:23 +08:00

    解决咯~

    在for里面写个go func,这样每次收到event的时候就能去开启一个goroutine去执行任务咯~ 这样写只是我一个人用而已没加消息队列。如果是大站的话不加估计会挂掉~

    https://i.loli.net/2020/07/11/a4EYdWTfKOH7lBs.png

    30 条回复    2020-07-11 15:56:05 +08:00
    knva
        1
    knva  
       2020-07-10 14:51:05 +08:00
    没做过,但是我觉得开个协程不就得了
    binux
        2
    binux  
       2020-07-10 14:56:37 +08:00 via Android
    执行什么?
    w99w
        3
    w99w  
       2020-07-10 14:59:41 +08:00
    你的请求是同时嘛?
    wunonglin
        4
    wunonglin  
    OP
       2020-07-10 14:59:57 +08:00
    @binux #2 执行了个 http 请求,我页面是 6 个任务同时发送的,但是后台是一个接一个执行,而不是同时执行 6 个任务
    wunonglin
        5
    wunonglin  
    OP
       2020-07-10 15:00:40 +08:00
    @w99w #3 对,页面请求是同时发出 6 个请求的,看时间戳就知道了
    takemeaway
        6
    takemeaway  
       2020-07-10 15:02:04 +08:00
    如果没有开协程和多线程,那么本身就是按顺序执行的。
    wunonglin
        7
    wunonglin  
    OP
       2020-07-10 15:07:55 +08:00
    @takemeaway #6 对,我是想过应该是这个问题。刚接触 go 还不太了解,没搜到有关 websocket 的教程或者例子,找到的那些都是聊天室的例子,但我这个只是 1v1 的
    BingoXuan
        8
    BingoXuan  
       2020-07-10 15:15:46 +08:00
    因为你函数写的是同步代码,你需要开协程去处理,并通过 chan 来收发结果。

    起码把代码发一下,不然 v 友都不知道你做了什么暗箱操作
    whileFalse
        9
    whileFalse  
       2020-07-10 15:24:00 +08:00
    LZ 的 Node 环境是不是开了多线程了。
    Node 本身是异步单线程的,加上多线程那就是乱序了……
    wunonglin
        10
    wunonglin  
    OP
       2020-07-10 15:24:19 +08:00
    @BingoXuan #8 代码我 append 了,貌似没有在线运行 go 的代码托管网站
    wunonglin
        11
    wunonglin  
    OP
       2020-07-10 15:27:59 +08:00
    @whileFalse #9 单线,异步的。转 go 之后不知道 go 这边要怎么写
    keepeye
        12
    keepeye  
       2020-07-10 15:30:48 +08:00
    第一张图每次 send 间隔好几毫秒啊 同时请求??
    wunonglin
        13
    wunonglin  
    OP
       2020-07-10 15:38:15 +08:00
    @keepeye #12

    也不能说死磕”同时“这个字眼,你发到后台去,按我理解的话它肯定是接收到一个就去执行一个,但是 go 这边像是被缓存住了,是一个执行完成后再去执行,而不是“接收到一个就去执行一个”。应该就是像#6 说的那样
    keepeye
        14
    keepeye  
       2020-07-10 15:43:54 +08:00
    @wunonglin 一个长连接本来就是顺序接收消息的啊,不知道 node 是怎么处理的,可能收到一条消息都生成 async task 类似 goroutine 吧
    xylophone21
        15
    xylophone21  
       2020-07-10 15:44:04 +08:00
    1. 看一下你给的链接里的 demo,https://github.com/labstack/echox/blob/master/cookbook/websocket/gorilla/server.go 。Subscribe 里 readLoop 不应该新开 go 程,直接卡在这里,直到 ws 断开。因为 Subscribe 是 HTTP Server 调的,你返回了,它就会认为这个连接已经结束了,虽然不一定会马上关(keep-alive),但必要的时候会断开。

    2. 作为一个 HTTP Server,Subscribe 应该是在不同的 go 程里被调用的,不然就就成了单协程了,你可以打印出来试一下
    wunonglin
        16
    wunonglin  
    OP
       2020-07-10 15:47:26 +08:00
    @keepeye #14 我是用 nest.js ,async 、await
    wunonglin
        17
    wunonglin  
    OP
       2020-07-10 15:52:11 +08:00
    @xylophone21 #15

    上面 append 的是我测试的代码,在跑着的代码是这样的 https://play.golang.org/p/4_Y4wUE3iNy,跟例子一样的,然后东搞搞西搞搞就成了上面那样
    xylophone21
        18
    xylophone21  
       2020-07-10 16:11:37 +08:00
    @wunonglin
    1.是否你的每个 for 在不同的 go 程,打印出来看看
    2. 加延迟,晚点发
    keepeye
        19
    keepeye  
       2020-07-10 16:12:29 +08:00
    @wunonglin nestjs 里面改成同步响应就和 go 一样了,或者 go 里面读到 body 的时候新建一个 goroutine 处理这样也就和 nestjs 里的异步响应效果一样。你用 nodejs 之所以习惯了异步处理是因为程序是单线程的,同步可能会长时间阻塞其他的请求,但在 go 里面,每个连接本身就在一个 goroutine 里,放心用同步的方式处理消息就是了,还能避免乱序问题
    wunonglin
        20
    wunonglin  
    OP
       2020-07-10 16:15:59 +08:00
    @keepeye #19 乱序是什么?
    keepeye
        21
    keepeye  
       2020-07-10 16:31:25 +08:00
    @wunonglin 按顺序处理每一条消息
    BingoXuan
        22
    BingoXuan  
       2020-07-10 16:40:09 +08:00
    @wunonglin
    我觉得你不要开协程去做 readloop,直接执行 readloop 。如果 CreateFromUrl 方法中涉及 io 处理或者可以并发执行的话,可以开协程执行并通过 chan 取回结果。参考: http://litang.me/post/golang-channel/#%E5%A4%9A%E5%86%99%E4%B8%80%E8%AF%BB
    wunonglin
        23
    wunonglin  
    OP
       2020-07-10 16:49:47 +08:00
    @xylophone21 #18

    https://i.loli.net/2020/07/10/CxVOXqPmNdyvZ5D.png

    打印出了 goroutine 和随机 Sleep 值
    wunonglin
        24
    wunonglin  
    OP
       2020-07-10 16:52:23 +08:00
    @keepeye #21 是这理没错,可是我想要按顺序多个一起执行
    Fitz
        25
    Fitz  
       2020-07-10 19:48:37 +08:00
    看到最后才看懂楼主的顺序执行是什么意思......同一个连接里收到的 event, 那可不就是顺序执行, 想要并发就在 for 循环里只要 ReadMessage 读到数据, 就启个 goroutine 去处理, 你 go c.readLoop(ctx)用错地方了.
    joesonw
        26
    joesonw  
       2020-07-10 22:40:25 +08:00
    @Fitz 不要教坏人家了. 弄一个 channel 缓存, 然后按照想要的并发量起 goroutine.
    wunonglin
        27
    wunonglin  
    OP
       2020-07-11 03:05:22 +08:00
    @Fitz #25
    @joesonw #26

    的确是 read 到数据就开个 goroutine 就行了
    hantsy
        28
    hantsy  
       2020-07-11 09:21:26 +08:00
    @wunonglin NestJS 不是应该用 Rxjs 多?
    wunonglin
        29
    wunonglin  
    OP
       2020-07-11 11:00:57 +08:00 via iPhone
    @hantsy 都有用,但是有些复杂的地方用 rxjs 也会嵌套太深
    hantsy
        30
    hantsy  
       2020-07-11 15:56:05 +08:00
    @wunonglin 最近玩了一下 Nestjs,如果没有 Rxjs 支持, 我根本就没兴趣。

    Nestjs 很多概念很源自 Angular,写 Angular 习惯了,async,await 和 promise 那一套实在用起来难受。Nestjs 内部几乎和 Angular 一致,都是 Rxjs 优先,但可惜,第三方集成很多还是基于 Promise 。

    https://github.com/hantsy/nestjs-sample 我这个的 Sample 除了 LocalStrategy 用了 Promise (用 Observable,需要额外转换),其它地方都是 Rxjs 。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2866 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 94ms · UTC 13:11 · PVG 21:11 · LAX 06:11 · JFK 09:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.