Una breve introducción a Django channels

in #spanish8 years ago (edited)

django-body13d301.jpg

Recientemente se publico el tutorial sobre Django channels, la última y más importante funcionalidad en los últimas versiones de Django. Uno de los problemas en los últimos años es que las tecnologías web han cambiado mucho y Django así como cualquier otro framework, se han quedados rezagados en un protocolo que próximamente será obsoleto, el http.

La web moderna utiliza cosas como http2 y websockets para demostrar la información en tiempo real. Haciendo que muchos de los beneficios de HTML 5 prometía hace algunos años. Sin embargo con la madurez de estas tecnologías Django debe modernizarse, y ahí es cuando Channels entra en juego. Channels es el modulo que lleva a Django a una web en tiempo real.

Como trabaja Channels?


Para entender Channels, primero debemos imaginarnos el siguiente escenario:

Una petición se lanza y alcanza nuestro servidor ngnix (proxy inverso). Ngnix pasa la petición a nuestro servidor de aplicaciones, en este punto ya nuestro servidor de aplicaciones esta trabajando con múltiples protocolos así que en vez de llamarlo servidor de aplicaciones, este se convierte en un servidor de interfaz. Nuestro servidor de interfaz acepta nuestra petición y la maneja como un mensaje. Entonces pasa nuestro mensaje a un canal (channels).

channelsprocess6a3bf.png

El siguiente paso es consumir estos mensajes asi que el programar receptores que acepten y procesen los mensajes sera el siguiente paso. Estos receptores reenviaran el mensaje a reply/response channel una vez terminado.

Una ves procesado y respondido, el servidor de interfaz lo enviara de vuelta al resto del mundo usando un response channel o canal de respuesta. Los receptores o *consumers trabajan en procesos en el fondo del servicio, podemos crear diferentes procesos hijos como necesitemos.

Esta es una forma muy sencilla de describir el proceso y como nuestra aplicación puede escalar a múltiples procesos de forma independiente y escalar. Recapitulando -- un servidor de interfaz acepta las peticiones y las acomoda como mensajes en canales. Los receptores o consumers las procesan en el fondo y las responden en los canales de respuesta o response channels. Y finalmente el servidor de interfaz envía la respuesta.

Existen canales que ya están disponibles a nosotros, por ejemplo el http.request. Puede ser usado para escuchar peticiones que son generadas del mensajes entrantes de canales http. O websocket.receive pueden ser usados para procesar mensajes entrantes de websockets. En realidad, no tiene mucho sentido en manejar http.request nosotros mismos, ya que podemos dejar que Django lo haga por nosotros.

Algo mas importante seria que manejaramos otros protocolos como conexiones por websockets. Ademas, con los canales ya incluidos, podríamos crear nuestros propios canales personalizados, para diferentes propósitos.

Con la base que creamos procesos que funcionan en el fondo del canal podemos meter tareas como por ejemplo la generación de imágenes en miniatura o thumbnails. En vez de hacerlo en tiempo real, se puede pasar a un proceso de fondo que lo vaya haciendo a su tiempo. Channels por defacto tiene un comando llamado runworker el cual puede ejecutar procesos de fondo que escuchan a estos canales.

El único problema es que no existe aun un comando para re-iniciar el mecanismo en caso de algún fallo o error. En este caso, Celery puede ser una excelente opción para escribir, ejecutar y administrar los receptores (workers), que procesaran los canales.

Daphne es el nombre del servidor de interfaz defacto que trabaja con Channels. Los channels y mensajes que se pasan sobre una capa de canales o (channel layers) que soporta multiples backends. Estos pueden ser Redis, en Memoria, IPC, etc.

Como ya te imaginaste tanto el backend como la capa de channels (channel layer) es usada para abstraer el proceso de manutención diferentes canales y colas permitiendo a los receptores para escucharlo. En el backend de memoria mantendrá a los canales en memoria para el desarrollo y para producción puede ser mantenida por un cluster de memoria en Redis.

Creemos un servidor de Websockets que haga eco


Suficiente de teoría procedamos con algo de acción, lo primero que haremos es instalar el módulo de channels:

pip install channels

Eso deberá instalar Django (ya que es una dependencia de channels) así como otros paquetes necesarios. Siguiente paso es iniciar nuestra app en django, usando django-admin.

Ahora agregaremos channels al área de INSTALLED_APPS en tu archivo de settings.py. Para desarrollo local, necesitaremos ingresar nuestra capa para channels llamada CHANNEL_LAYERS, insertandolas en settings.py:

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "asgiref.inmemory.ChannelLayer",
        "ROUTING": "realtime.routing.channel_routing",
    },
}

Configurando el ruteo de los mensajes



En el código anterior, favor de insertar la llave de routing. El valor de esta llave, deberemos pasar la ruta de nuestro canal de ruteo. En este caso, el app se llamará realtime con un modulo llamado routing.py el cual contiene el routing del canal:

from channels.routing import route
from .consumers import websocket_receive
 
channel_routing = [
    route("websocket.receive", websocket_receive, path=r"^/chat/"),
]

En la lista de ruteo de Channels, definimos nuestro route el cual se asemejan mucho a los patrones de URL de Django. Cuando recibimos los mensajes de nuestra conexión de websockets, el mensaje es pasado en el canal de websockets.receive. Así que definimos a consumer para que consuma los mensajes del canal.

Definiendo el receptor



También definimos una ruta para indicar la conexión de websockets /chat/ la cual se manejara por esta ruta en particular. Si llegáramos a omitir la ruta, el cliente podría conectarse a cualquier URL del host y podríamos cacharlos todos! Pero si definimos una ruta, nos ayudará con los namespaces y otras situaciones que veremos más adelante.

Aquí mostramos el código de consumer.py:

def websocket_receive(message):
    text = message.content.get('text')
    if text:
        message.reply_channel.send({"text": "You said: {}".format(text)})

Este consumidor es muy basico ya que solo obtiene el texto del websocket y lo contesta de vuelta. Nota que el contenido del websocket esta incluido el atributo de content del mensaje. Y que el reply_channel es el canal de respuesta ahí (el servidor de interfaz esta escuchando en los canales). Cuando sea que querríamos enviar a estos canales es pasado de vuelta a la conexión de websockets.

Continuaremos en otro articulo la ejecución del servicio y las posibles personalizaciones....


JZA

About me:
Steemer, crypto fan, like to listen to 90s hip hop, and loves to chat about Linux Python and Free software. Runs a local Tech club in sunny Cancun, and enjoys hoping on planes and landing somewhere else.

Coin Marketplace

STEEM 0.18
TRX 0.14
JST 0.029
BTC 58051.31
ETH 3136.86
USDT 1.00
SBD 2.44