Websockets #257
-
Hi @gi0baro What would be missing in emmett to be able to do this [1]. Websockets and something else? [1] https://programadorwebvalencia.com/tutorial-flask-para-crear-chat-con-socketio-y-vuejs/ |
Beta Was this translation helpful? Give feedback.
Replies: 9 comments
-
Hi @josejachuf, Mind that I never used socketio, but inspecting the snippets in the link you provided, the websocket implementation is a bit low level, so to implement a chat I'll probably use an external pub-sub provider like redis, and bind the sockets to it in order to push new messages. |
Beta Was this translation helpful? Give feedback.
-
@gi0baro |
Beta Was this translation helpful? Give feedback.
-
@josejachuf sorry for the delay, I was a little busy in the last days. BTW, here is how I implemented it, but please consider that sockets get used only to get new messages in real time, since for posting new messages I think a standard POST request is better. Also, this is implemented using redis since it saves quite a lot of coding. First, you need an async implementation of redis (based on from aioredis import Redis as _Redis
from aioredis.pool import ConnectionsPool
from contextlib import asynccontextmanager
from emmett.extensions import Extension, listen_signal
class Redis(Extension):
def on_load(self):
self.pool = None
@listen_signal('after_loop')
def build_pool(self, loop):
self.pool = ConnectionsPool(
f'redis://{self.app.config.redis.host}:{self.app.config.redis.port}',
db=0,
minsize=0,
maxsize=128,
create_connection_timeout=1,
loop=loop
)
@asynccontextmanager
async def ctx(self):
connection = await self.pool.acquire()
try:
yield _Redis(connection)
finally:
self.pool.release(connection) put the redis config in app.use_extension(RedisExtension) and then you can write your socket: @app.websocket('/chat/<str:room>')
async def chat_messages(room):
async with app.ext.Redis.ctx() as redis:
channel, = await redis.subscribe(f'chat:{room}:messages')
await websocket.accept()
async for message in channel.iter():
await websocket.send(message) Now the only thing you need to do is to write the message to the redis channel after saving the record in your standard route, eg: from emmett.serializers import Serializers
json_dump = Serializers.get_for('json')
@app.route('/chat/<str:room>/messages', methods=['post'])
async def new_chat_message(room):
response.status = 201
params = await request.body_params
# maybe do some parsing?
res = ChatMessage.create(**params)
if res.errors:
response.status = 422
return {'error': res.errors.as_dict()}
row = ChatMessage.get(res.id)
async with app.ext.Redis.ctx() as redis:
await redis.publish(f'chat:{room}:messages', json_dump(row))
return row It should work. Please mind that sockets shares the same pipeline of the routes, so if you put database pipe in the application pipeline it will open up connections also for every socket connection. My suggestion is to split pipelines with modules, something like: routes = app.module(__name__, 'routes')
sockets = app.module(__name__, 'sockets')
routes.pipeline = [db.pipe] I think is a good case for creating a proper example in the examples folder. |
Beta Was this translation helpful? Give feedback.
-
Thanks @gi0baro |
Beta Was this translation helpful? Give feedback.
-
Hi @gi0baro This ws is very new to me, I am trying to understand. When I try with (plugin ws client tester) here I get an error
As expected I get:
|
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
Hi @gi0baro
I am quite lost. This is a simplification of your example. It works without errors. |
Beta Was this translation helpful? Give feedback.
-
I got to that:
|
Beta Was this translation helpful? Give feedback.
-
Hello The modifications I made were these
I get this error:
|
Beta Was this translation helpful? Give feedback.
@josejachuf sorry for the delay, I was a little busy in the last days.
BTW, here is how I implemented it, but please consider that sockets get used only to get new messages in real time, since for posting new messages I think a standard POST request is better. Also, this is implemented using redis since it saves quite a lot of coding.
First, you need an async implementation of redis (based on
aioredis
package):