socketio - Cliente Python Socket.IO para enviar mensajes de difusión al servidor TornadIO2
socket io java (3)
Estoy construyendo una aplicación web en tiempo real. Quiero poder enviar mensajes de difusión desde la implementación del lado del servidor de mi aplicación python.
Aquí está la configuración:
- socketio.js en el lado del cliente
- Servidor TornadIO2 como servidor Socket.IO
- Python en el lado del servidor (marco Django )
Puedo enviar con éxito mensajes de socket.io del cliente al servidor. El servidor maneja estos y puede enviar una respuesta. A continuación describiré cómo hice eso.
Configuración actual y código
Primero, necesitamos definir una conexión que maneje eventos socket.io:
class BaseConnection(tornadio2.SocketConnection):
def on_message(self, message):
pass
# will be run if client uses socket.emit(''connect'', username)
@event
def connect(self, username):
# send answer to client which will be handled by socket.on(''log'', function)
self.emit(''log'', ''hello '' + username)
El inicio del servidor se realiza mediante un método personalizado de administración de Django:
class Command(BaseCommand):
args = ''''
help = ''Starts the TornadIO2 server for handling socket.io connections''
def handle(self, *args, **kwargs):
autoreload.main(self.run, args, kwargs)
def run(self, *args, **kwargs):
port = settings.SOCKETIO_PORT
router = tornadio2.TornadioRouter(BaseConnection)
application = tornado.web.Application(
router.urls,
socket_io_port = port
)
print ''Starting socket.io server on port %s'' % port
server = SocketServer(application)
Muy bien, el servidor se ejecuta ahora. Añadamos el código de cliente:
<script type="text/javascript">
var sio = io.connect(''localhost:9000'');
sio.on(''connect'', function(data) {
console.log(''connected'');
sio.emit(''connect'', ''{{ user.username }}'');
});
sio.on(''log'', function(data) {
console.log("log: " + data);
});
</script>
Obviamente, {{ user.username }}
será reemplazado por el nombre de usuario del usuario que ha iniciado sesión actualmente, en este ejemplo el nombre de usuario es "alp".
Ahora, cada vez que la página se actualiza, la salida de la consola es:
connected
log: hello alp
Por lo tanto, invocar mensajes y enviar respuestas funciona. Pero ahora viene la parte difícil.
Problemas
La respuesta "hello alp" se envía solo al invocador del mensaje socket.io. Quiero transmitir un mensaje a todos los clientes conectados, para que puedan informarse en tiempo real si un nuevo usuario se une al participante (por ejemplo, en una aplicación de chat).
Asi que aqui están mis preguntas:
¿Cómo puedo enviar un mensaje de difusión a todos los clientes conectados?
¿Cómo puedo enviar un mensaje de difusión a varios clientes conectados que están suscritos en un canal específico?
¿Cómo puedo enviar un mensaje de difusión a cualquier lugar en mi código de Python (fuera de la clase
BaseConnection
)? ¿Esto requeriría algún tipo de cliente Socket.IO para python o está integrado en TornadIO2?
Todas estas transmisiones deben realizarse de manera confiable, por lo que supongo que los websockets son la mejor opción. Pero estoy abierto a todas las buenas soluciones.
Escribo aquí, porque es difícil escribir en la sección de comentarios. Puede ver ejemplos de tornadoio2 en el directorio de ejemplos donde puede encontrar la implementación del chat, y:
class ChatConnection(tornadio2.conn.SocketConnection):
# Class level variable
participants = set()
def on_open(self, info):
self.send("Welcome from the server.")
self.participants.add(self)
def on_message(self, message):
# Pong message back
for p in self.participants:
p.send(message)
Como se puede ver, implementaron a los participantes tal como fueron establecidos))
Recientemente he escrito una aplicación muy similar en una configuración similar, así que tengo varias ideas.
La forma correcta de hacer lo que necesita es tener un backend pub-sub. Hay tanto que puedes hacer con los ConnectionHandler
simples. Finalmente, el manejo de conjuntos de conexiones de nivel de clase comienza a ponerse feo (por no mencionar el buggy).
Idealmente, querría usar algo como Redis, con enlaces asíncronos al tornado (consulte brukva ). De esa manera, no tiene que meterse con el registro de clientes en canales específicos, Redis tiene todo eso listo para usar.
Esencialmente, tienes algo como esto:
class ConnectionHandler(SockJSConnection):
def __init__(self, *args, **kwargs):
super(ConnectionHandler, self).__init__(*args, **kwargs)
self.client = brukva.Client()
self.client.connect()
self.client.subscribe(''some_channel'')
def on_open(self, info):
self.client.listen(self.on_chan_message)
def on_message(self, msg):
# this is a message broadcast from the client
# handle it as necessary (this implementation ignores them)
pass
def on_chan_message(self, msg):
# this is a message received from redis
# send it to the client
self.send(msg.body)
def on_close(self):
self.client.unsubscribe(''text_stream'')
self.client.disconnect()
Tenga en cuenta que usé sockjs-tornado que encontré mucho más estable que socket.io.
De todos modos, una vez que tenga este tipo de configuración, enviar mensajes desde cualquier otro cliente (como Django, en su caso) es tan fácil como abrir una conexión Redis ( redis-py es una apuesta segura) y publicar un mensaje:
import redis
r = redis.Redis()
r.publish(''text_channel'', ''oh hai!'')
Esta respuesta resultó bastante larga, así que hice un esfuerzo adicional e hice una publicación de blog: http://blog.y3xz.com/blog/2012/06/08/a-modern-python-stack-for-a-real-time-web-application/
Si ya está usando django, por qué no echa un vistazo a gevent-socketio.