V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
chaleaochexist
V2EX  ›  程序员

websocket 方案求助 -- 如何实现实时进度提交给前端

  •  
  •   chaleaochexist · 2019-09-26 17:17:29 +08:00 · 5640 次点击
    这是一个创建于 1645 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前端点一个按钮, 发送一个 ajax, 后端开始做动作,同时不停的将处理进度实时推送给前端.

    现在的思路是, 前端和后端 ajax 同时绑定同一个 group. 后端给 group 发消息, websocket 接什么消息发什么消息,向同一个 group. 这样前端就收到消息了.

    def view(request):
        ws = websocket(***)
        # do something business logic
        ws.send()
        return 666
    

    伪代码大概是这样的,但是 websocket server 端的心跳机制会导致和 view 里面创建的 ws 客户端失去连接.

    现在的解决方案是, 在 view 里面创建一个线程, 在新起的线程里面 不停的发消息给 websocket server.

    拍脑袋感觉,应该在 view 里面做一个非阻塞带回调的 websocket 客户端,但是...如何实现呢...

    第 1 条附言  ·  2019-09-27 07:11:49 +08:00

    实际效果 逻辑框图

    30 条回复    2019-09-27 13:06:53 +08:00
    halk
        1
    halk  
       2019-09-26 19:05:29 +08:00
    1. websocket server 端的心跳机制会导致和 view 里面创建的 ws 客户端失去连接.
    --这句话怎么理解?

    2. 试试 SSE,能满足你的需求吗
    wolfie
        2
    wolfie  
       2019-09-26 19:17:59 +08:00
    ws 不是长连接吗,为什么会断开。

    客户端定时检查连接是否可用,断了就重连。
    Carseason
        3
    Carseason  
       2019-09-26 19:25:53 +08:00
    websocket 是长连接,你心跳也不需要把当前的断开重连吧?
    服务端应该把客户端存到一个队列里面,然后当服务端接收到新消息后对该队列推送消息来实现广播
    hantsy
        4
    hantsy  
       2019-09-26 20:39:54 +08:00
    SSE 比较适合
    kxiaong
        5
    kxiaong  
       2019-09-26 20:57:06 +08:00
    拍脑袋想, 你这样子连 websocket 链接也创建不起来吧?

    并不是你在 server 端起一个 websocket server,然后不停的 send,客户端就会接收数据。 在 websocket 链接创建之前还有 http 报文做协商认证和链接过程。

    websocket 的第一个报文是这样的 (ietf rfc 7977):
    ```
    GET / HTTP/1.1
    Host: a.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Origin: http://www.example.com
    Sec-WebSocket-Protocol: msrp
    Sec-WebSocket-Version: 13
    ```

    一个可能的办法是: 页面先跟 server 创建一个 websocket 链接,使用 groupId 标识这个链接。

    页面点击动作的 ajax 将自己的 groupId 作为参数传入 view。view 触发业务逻辑以后就返回 ok, 不需要阻塞等待。

    业务逻辑处理过程中的进度信息,通过 websocket 推送到 client
    neoblackcap
        6
    neoblackcap  
       2019-09-26 21:01:42 +08:00 via iPhone
    websocket 之类的长连接就该用 Tornado 之类的框架,一般基于 request-response 的框架当然会断
    KuroNekoFan
        7
    KuroNekoFan  
       2019-09-26 21:03:53 +08:00 via iPhone
    你这提问看得我一头雾水
    21paradox
        8
    21paradox  
       2019-09-26 21:17:37 +08:00
    用 server send events 吧,sse 或者前端轮询
    mengqi
        9
    mengqi  
       2019-09-26 21:31:58 +08:00
    看了几遍题主的说明,最后终于确认:没看懂…
    1. 为什么前后端不能直接建立 websocket 长连接,而需要一个 group ?
    2. “server 端的心跳机制”是指服务器向客户端发送心跳还是客户端向服务端发送心跳?又为什么会导致客户端断开连接?
    3. 你贴的伪代码是服务端的代码吗?为什么后面的解决方案又说“在 view 里面创建一个线程, ……不停的发消息给 websocket server”?
    zhuzhibin
        10
    zhuzhibin  
       2019-09-26 22:12:30 +08:00 via iPhone
    连接成功做你需要做的事 如果断了自己做一次重连
    izoabr
        11
    izoabr  
       2019-09-26 23:15:19 +08:00
    我是直接用 rabbitmq 的 websocket,然后后台和前台都连一下,前台监控队列做回调显示进度就好了。
    chaleaochexist
        12
    chaleaochexist  
    OP
       2019-09-27 07:04:56 +08:00
    @mengqi 我当时问这个问题的时候就想到了可能看不懂...

    一会儿主贴补一个框图...供您参考.
    1. 为什么前后端不能直接建立 websocket 长连接,而需要一个 group ?
    前端 后端 wsserver 我们现在有这三个实例, 后端需要将消息发送给前端 通过 ws. name 后端和 ws 之间需要通信,我们通过 ws.
    否则 ws 后端接到的消息如何推送给前端?
    2. “server 端的心跳机制”是指服务器向客户端发送心跳还是客户端向服务端发送心跳?又为什么会导致客户端断开连接?
    这个是 server 向 client 发送心跳, 断开连接是服务端(daphne)的配置,心跳间隔 20s.超过 20s 就主动断开连接. 修改心跳间隔是解决思路之一,但是目前我们并不想采用.
    3. 你贴的伪代码是服务端的代码吗?为什么后面的解决方案又说“在 view 里面创建一个线程, ……不停的发消息给 websocket server”?
    是后端的代码,后端属于 ws 客户端. 前端也是 ws 客户端.
    因为,如果客户端不停的给 ws 服务端,那么服务端就不会,也不需要发送心跳了.
    chaleaochexist
        13
    chaleaochexist  
    OP
       2019-09-27 07:13:49 +08:00
    @neoblackcap 可不可以把一个 ws client 做成 异步形式?
    现在的项目就是用 django 做的,不可能为了一个 api 换框架啊.代价大了点.
    chaleaochexist
        14
    chaleaochexist  
    OP
       2019-09-27 07:18:40 +08:00
    @wolfie
    客户端定时检查是指前端定时检查吗? 前端没有问题,浏览器会替我们发送心跳包.
    我们现在遇到的问题是另一个客户端 -- http 后端. 定时检查如何处理?我们现在是起了一个线程.
    @Carseason
    这个是框架的做法.我也没好办法.
    @kxiaong
    我就是这么做的. 但是遇到了 ws 服务端 20 秒会发送心跳包给 http 服务端. 我现在需要在 http 服务端 view 里面做一个非阻塞最好是异步的 ws 客户端. 除了起新线程, 有没有更好的办法.譬如用 gevent 有没有例子.
    neoblackcap
        15
    neoblackcap  
       2019-09-27 07:33:54 +08:00 via iPhone
    @chaleaochexist 那你去用 Django 官方出的 channel 啊
    Nasei
        16
    Nasei  
       2019-09-27 08:12:39 +08:00 via Android
    这么麻烦的吗…进度更新只用过 netcore 的 signalR,基本傻瓜式操作
    zazalu
        17
    zazalu  
       2019-09-27 08:39:40 +08:00
    没看懂+1 不过我是因为菜没看懂 mark 下
    longkas
        18
    longkas  
       2019-09-27 08:55:21 +08:00 via Android
    b821025551b
        19
    b821025551b  
       2019-09-27 08:59:10 +08:00   ❤️ 1
    这个是 server 向 client 发送心跳, 断开连接是服务端(daphne)的配置,心跳间隔 20s.超过 20s 就主动断开连接. 修改心跳间隔是解决思路之一,但是目前我们并不想采用.
    -----------------------------------------------

    怎么总觉得怪怪的?正常心跳 1s 一个,20s 是没收到心跳就断了;你这是设置了 40s 一个心跳然后 20s 没心跳就断了?
    不想修改心跳是什么鬼?
    还有,即使是断了,再请求一个重连不就行了?想那么麻烦干吗?
    chaleaochexist
        20
    chaleaochexist  
    OP
       2019-09-27 09:12:47 +08:00
    @b821025551b 谢谢,最后一句话有用.我没想到.

    心跳就是 20s 间隔.我看代码默认是这个配置.超过 20S(这个是心跳间隔)+30S(这个是 timeout) 就会主动断开连接.
    忘记是 channel 还是 daphne 的代码了.\
    ```
    class Server():
    def __init__():
    pass # 就在这里
    ```

    @neoblackcap 谢谢我用的就是 channel,另外本问题和 ws server 端没有太大关系,我的问题是如何做一个异步非阻塞的 ws 客户端.channel 还支持客户端吗?像 aiohttp 那样?
    tanszhe
        21
    tanszhe  
       2019-09-27 09:16:10 +08:00
    这么简单的问题 ,搞不清楚 ……
    MonoLogueChi
        22
    MonoLogueChi  
       2019-09-27 09:31:29 +08:00 via Android
    @Nasei signalR 吹?我也喜欢用
    LeeSeoung
        23
    LeeSeoung  
       2019-09-27 09:36:33 +08:00
    ws 不是双向的么。。没搞明白你想做啥。。为啥 ws 会自己断。。
    chaleaochexist
        24
    chaleaochexist  
    OP
       2019-09-27 09:52:50 +08:00
    @LeeSeoung 和本题无关的一种可能, timeout 不就断了吗?很好理解.
    shuizhengqi
        25
    shuizhengqi  
       2019-09-27 10:38:58 +08:00
    直接 setInterval 不好使么?
    shuizhengqi
        26
    shuizhengqi  
       2019-09-27 10:39:39 +08:00
    1s 一次,就能做到跟实时差不多的效果
    zpf
        27
    zpf  
       2019-09-27 11:34:47 +08:00
    前端不应该直接请求 websocket 服务器,为什么还要先请求 view 层,view 层在做请求转发?
    chaleaochexist
        28
    chaleaochexist  
    OP
       2019-09-27 12:20:36 +08:00
    @shuizhengqi 当然可以 setInterval 直接发 ajax 就可以了,和 websocket 有啥关系?

    @zpf 实时推送后台的处理进度.
    view 在做一个业务逻辑,并将业务逻辑的进度发送给前端.
    你的意思是说,这个业务逻辑不用 ajax 而是用 websocket 来实现是吗?
    hsfzxjy
        29
    hsfzxjy  
       2019-09-27 12:25:18 +08:00 via Android
    django channels,了解一下
    chaleaochexist
        30
    chaleaochexist  
    OP
       2019-09-27 13:06:53 +08:00
    @hsfzxjy 谢谢我用的就是 channel,另外本问题和 ws server 端没有太大关系,我的问题是如何做一个异步非阻塞的 ws 客户端.channel 还支持客户端吗?像 aiohttp 那样?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2718 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 00:21 · PVG 08:21 · LAX 17:21 · JFK 20:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.