-
-
Notifications
You must be signed in to change notification settings - Fork 951
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Broadcast interface #133
Comments
We’ll want a general interface, which alternative backend implementions can then be plugged into. |
I tried something hacky to get something going, but I'm a bit out of my league here. This works for client@localhost (sending/receiving all), and client@anotherhost (sending, but not receiving) import uvicorn
from starlette.applications import Starlette
from starlette.endpoints import WebSocketEndpoint, HTTPEndpoint
from starlette.responses import HTMLResponse, JSONResponse
from starlette.middleware.cors import CORSMiddleware
from collections import defaultdict
from starlette.websockets import WebSocketState
app = Starlette()
app.add_middleware(
CORSMiddleware, allow_origins=["*"], allow_headers=["*"], allow_methods=["*"]
)
html = """
<!DOCTYPE html>
<html>
<head>
<title>Chat</title>
</head>
<body>
<h1>WebSocket Chat</h1>
<form action="" onsubmit="sendMessage(event)">
<input type="text" id="messageText" autocomplete="off"/>
<button>Send</button>
</form>
<ul id='messages'>
</ul>
<script>
var ws = new WebSocket("ws://"+location.host+"/ws");
ws.onmessage = function(event) {
var messages = document.getElementById('messages')
var message = document.createElement('li')
var content = document.createTextNode(event.data)
message.appendChild(content)
messages.appendChild(message)
};
function sendMessage(event) {
var input = document.getElementById("messageText")
ws.send(input.value)
input.value = ''
event.preventDefault()
}
</script>
</body>
</html>
"""
@app.route("/")
class Homepage(HTTPEndpoint):
async def get(self, request):
return HTMLResponse(html)
@app.websocket_route("/ws")
class Broadcast(WebSocketEndpoint):
encoding = "text"
sessions = {}
def update_sess_data(self, ws, data):
sess_key = ws.headers.get('sec-websocket-key', 'last')
self.sessions[sess_key] = ws
self._reap_expired_sessions()
async def broadcast_message(self, msg):
for k in self.sessions:
ws = self.sessions[k]
await ws.send_text(f"message text was: {msg}")
def _reap_expired_sessions(self):
expired = []
for k in self.sessions:
sess = self.sessions[k]
if sess.client_state != WebSocketState.CONNECTED:
expired.append(k)
print('removing expired session:', k)
self.sessions = {k: self.sessions[k] for k in self.sessions if k not in expired}
async def on_receive(self, ws, data):
self.update_sess_data(ws, data)
await self.broadcast_message(data)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000) |
Hello @rcox771, as chat i prefer to have sessions as global app var that will be appended on
I thought about doing some examples on my github to help the community, also i have little time to do it. |
How would you publish messages outside the context of the web socket? For example, if a user updated their profile, and you wanted to broadcast that new information to all connected channels, how would you go about that? Is it required to use a separate process? Would you have to continually poll some service (like redis)? |
@3lpsy |
@3lpsy You need a broadcast service of one kind or aother...
|
Thanks @tomchristie and @DrPyser for the advice. For anyone else attempting to do this, I created a working implementation using aioredis and FastAPI(an extension of Starlette). I have serious concerns over thread safety and other things I'm doing wrong but it works at least. Below is some sample code but you can see the entire project here: https://github.com/3lpsy/bountydns/ A few code snippets:
With this setup, I can push messages to all authenticated (or unauthenticated users) in route functions like this:
I also wanted to listen for sqlalchemy's "after_insert" event. However, I had to attach the async method call to the event loop. It may be incorrect but it works as of now:
And here is an example of the event handler in the model class:
Hope this helps anyone looking to do something similar. On the front end, I create two websockets and then emit them using a Vuejs bus. You can check out the project if you're interested. And if you have any feedback on things I'm doing wrong, please let me know. |
If I've got this right, all the implementations here use python loops over all connected users. Is there a possibility that we can get Cython or C loops inbuilt into the library to do this without the python loop overhead, please? |
The broadcaster project is a working approach to this. (Although it's stretched a bit thin on maintenance ATM) https://github.com/encode/broadcaster |
WebSockets and SSE aren’t really any use without a broadcast interface. (Eg redis pub/sub or postgres listen/notify)
Look to channels’ group add/discard/send here.
The text was updated successfully, but these errors were encountered: