Sanic Webserver:Websocket处理程序在返回时关闭套接字;循环中断其他请求处理程序

Sanic Webserver:Websocket处理程序在返回时关闭套接字;循环中断其他请求处理程序,websocket,sanic,Websocket,Sanic,场景:我有一个sanic Web服务器为一个简单的网站服务。该网站基本上是一个支持vue模板的html大数据表。由于表条目每隔几分钟更改一次,因此在更改时通过websocket传递数据。同时大约有2000名用户。我试图实现一个发布/订阅架构 问题:我的sanic处理程序一返回,我的WebSocket就关闭了。我可以在里面有一个循环来保持处理器打开。但是让2000个处理器保持打开状态听起来是个坏主意。。。此外,open处理程序的行为也很奇怪。一个线程或一个小线程池应该可以完成这项工作。也许我把sa

场景:我有一个sanic Web服务器为一个简单的网站服务。该网站基本上是一个支持vue模板的html大数据表。由于表条目每隔几分钟更改一次,因此在更改时通过websocket传递数据。同时大约有2000名用户。我试图实现一个发布/订阅架构

问题:我的sanic处理程序一返回,我的WebSocket就关闭了。我可以在里面有一个循环来保持处理器打开。但是让2000个处理器保持打开状态听起来是个坏主意。。。此外,open处理程序的行为也很奇怪。一个线程或一个小线程池应该可以完成这项工作。也许我把sanic的文档弄错了,需要设计建议

我尝试过的事情: -将超时设置增加到足够高 -在sanic中尝试各种其他websocket设置 -让我的客户端js在消息()上返回false -传递ws引用后,将其设置为null

Sanic Web服务器的索引:

@app.route(“/”)
异步def SERVER_索引(请求):
return wait file(os.path.join(os.path.dirname(_文件,'index.html'))
html的JS:

var app = new Vue({
    el: '#app',
        data() {
            manydata0: 0,
            manydata1: 0,
            ws: null,
        }
    },
    methods: {
        update: function (json_data) {
            json = JSON.parse(json_data);
            this.manydata0 = json['data0'];
            this.manydata1 = json['data1'];
        }
    },
    created: function () {
        this.ws = new WebSocket('ws://' + document.domain + ':' + location.port + '/reload');
        messages = document.createElement('ul');
        this.ws.onmessage = function (event) {
            console.log("new data")
            app.update(event.data);
        return false;
    };
    document.body.appendChild(messages);
    this.ws.onclose = function (event) {
        console.log("closed :(")
    };
Sanic Webserver的Websocket处理程序(第1版,套接字立即死亡):

@app.websocket(“/reload”)
异步def提要(请求,ws):
#time.sleep(42)#这会导致在我请求42秒后在客户端创建并关闭websocket
等待ws.send(Path(json.read_text())#服务初始数据
已连接的客户端。附加(ws)#订阅websocket列表。另一个线程将读取列表条目并为其提供更新
Sanic WebServer的Websocket处理程序(第二版,处理程序阻止其他req处理程序)

@app.websocket(“/reload”)
异步def提要(请求,ws):
mod_时间=0
尽管如此:
尝试:
stat=os.stat(json)
如果mod_时间!=统计时间:
等待ws.send(路径(json.read_text())
例外情况除外,如e:
打印(“检查文件时出现异常:”,e)
#这将停止服务器处理其他@app.route,如css、字体、favicon
Sanic WebServer的Websocket处理程序(第3个版本,不必要的recv())

@app.websocket(“/reload”)
异步def提要(请求,ws):
mod_时间=0
尽管如此:
尝试:
stat=os.stat(json)
如果mod_时间!=统计时间:
等待ws.send(路径(json.read_text())
等待recv()#如果客户端不时发送,则一切正常
例外情况除外,如e:
打印(“检查文件时出现异常:”,e)
最后两段代码差别不大。我添加了一个ws.recv()并从客户端发送一些合适的东西(例如,在一段时间间隔内),然后一切正常。然后发送css、字体和favicon。但这不是故意的,不是吗?这应该不能很好地扩展,对吗


总而言之,这对我来说没有多大意义。我误解了什么?

这里的Sanic核心开发人员之一

首先,我准备了一个pubsub类型体系结构的示例。我想这可能会有帮助

我的基本想法是创建一个
Feed
对象,它在自己的任务中循环查找事件。就我而言,这是从pubsub接收信息。在您的情况下,应该检查JSON文档上的时间

然后,当该
Feed.receiver
触发了一个事件时,它将ping出到所有正在侦听的客户端

websocket
处理程序本身中,您希望保持其打开状态。如果没有,则连接将关闭。如果您不关心从客户端接收信息,则不需要使用
wait recv()


因此,在您的情况下,使用SUPER简单逻辑,我将执行以下操作

这是未经测试的代码,可能需要一些调整

导入操作系统
随机输入
导入字符串
从functools导入部分
从pathlib导入路径
从sanic进口sanic
导入异步
导入WebSocket
从数据类导入数据类,字段
从键入导入可选,设置
app=Sanic(名称)
FILE=“/tmp/foobar”
超时=10
间隔=20
def生成代码(长度=12,包含标点符号=False):
字符=string.ascii_字母+string.digits
如果包含标点符号:
字符+=字符串。标点符号
返回“”。在范围(长度)内的x中加入(随机选择(字符))
@数据类
类客户端:
接口:websockets.server.WebSocketServerProtocol=字段(repr=False)
sid:str=字段(默认工厂=部分(生成代码,36))
定义散列(自我):
返回散列(str(self))
异步def保持_活动(自)->无:
尽管如此:
尝试:
尝试:
pong_water=等待self.interface.ping()
wait asyncio.wait_for(pong_water,timeout=timeout)
除asyncio.TimeoutError外:
打印(“无乒乓!!”)
等待self.feed.unregister(self)
其他:
打印(f“ping:{self.sid}on”)
等待异步睡眠(间隔)
除websockets.exceptions.ConnectionClosed外:
打印(f“断开的连接:{self.sid}on”)
等待self.feed.unregister(self)
打破
异步def关闭(自)->无:
self.interface.close()
异步def运行(自)->无:
尝试:
self.feed.app.add_任务(self.keep_alive())
尽管如此:
通过
除websockets.exceptions.ConnectionClosed外:
打印(“连接关闭”)
最后:
等待self.feed.unregister(self)
类提要:
app:Sanic
客户端:设置[客户端]
缓存=无
定义初始化(self,应用程序:Sanic):
self.clients=set()
self.app=app
@类方法
异步def get(cls,应用程序:Sanic):
是否存在=错误