Scenario: I have a sanic webserver serving a simple website. The website is basically a large data table in html with vue template support. Since the table entries change every few minutes, the data is delivered via websocket on change. Around 2000 users at the same time. I have tried to implement a pub/sub architecture.
Problem: My websockets are closed as soon as my sanic handler returns. I could have a loop inside to keep the handler open. But keeping 2000 handlers open sounds like a bad idea... Also the open handlers behave strange. One thread or a small threadpool should do the job. Maybe I got the sanic documentation wrong and need design advice.
Things I've tried: - increasing the timeout setting to be high enough - trying various other websocket settings in sanic - let my client side js return false onmessage (Javascript websockets closing immediately after opening) - set the ws reference to null after passing it
Sanic Webserver's Index:
@app.route('/')
async def serve_index(request):
return await file(os.path.join(os.path.dirname(__file__), 'index.html'))
Index.html's 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's Websocket Handler (1st Version, Sockets die immediately):
@app.websocket('/reload')
async def feed(request, ws):
#time.sleep(42) # this causes the websocket to be created and closed on client side 42 seconds after my request
await ws.send(Path(json).read_text()) # serve initial data
connected_clients.append(ws) # subscribe to websocket list. another thread will read list entries and serve them updates
Sanic Webservers's Websocket Handler (2nd Version, Handler blocks other req handlers)
@app.websocket('/reload')
async def feed(request, ws):
mod_time = 0
while True:
try:
stat = os.stat(json)
if mod_time != stat.st_mtime:
await ws.send(Path(json).read_text())
except Exception as e:
print("Exception while checking file: ", e)
# this stops the server to handle other @app.routes like css, fonts, favicon
Sanic Webservers's Websocket Handler (3nd Version, unnecessary recv())
@app.websocket('/reload')
async def feed(request, ws):
mod_time = 0
while True:
try:
stat = os.stat(json)
if mod_time != stat.st_mtime:
await ws.send(Path(json).read_text())
await recv() # if the client sends from time to time all is fine
except Exception as e:
print("Exception while checking file: ", e)
The last two code snippets don't differ much. I add a ws.recv() and send some fitting stuff from client side (in an interval for example), then everything works. Then css, fonts and favicon are sent. But that cannot be intended, can it? This should not scale well, right?
All in all that does not make much sense to me. What am I missunderstanding?