Este site usa cookies e tecnologias afins que nos ajudam a oferecer uma melhor experiência. Ao clicar no botão "Aceitar" ou continuar sua navegação você concorda com o uso de cookies.

Aceitar
Django Channels | Trabalhando com WebSockets e recursos assíncronos para recursos em tempo real

django

Django Channels | Trabalhando com WebSockets e recursos assíncronos para recursos em tempo real

Elias
Escrito por Elias
Junte-se a mais de X pessoas

Entre para nossa lista e receba conteúdos exclusivos e com prioridade

Imagine que você tem uma aplicação Django rodando tranquilamente, lidando com requisições HTTP de forma tradicional. Mas, de repente, surge a necessidade de um recurso em tempo real: talvez um chat ao vivo, notificações em tempo real ou uma aplicação que precisa de atualizações dinâmicas sem que o usuário recarregue a página.

Para a maioria dos desenvolvedores, o primeiro pensamento é: “Como eu vou fazer isso com o Django? O Django não foi feito para isso!”. E aí, você descobre o Django Channels.

django channels

O Django Channels expande o Django tradicional, permitindo que você trabalhe com protocolos como WebSockets, HTTP2, e até mesmo com tarefas de longa duração, utilizando recursos assíncronos. Isso significa que agora você pode criar aplicações que funcionam em tempo real, integrando-as ao seu projeto Django já existente.

WebSockets, ao contrário de HTTP, permitem uma comunicação bidirecional entre o servidor e o cliente. Em vez de esperar que o cliente faça uma requisição para receber os dados, com WebSockets, o servidor pode enviar informações diretamente para o cliente assim que algo acontece.

É como se o servidor tivesse um telefone e pudesse ligar para o cliente assim que necessário, sem esperar o cliente ligar primeiro. No contexto de recursos em tempo real, isso é essencial.

E é aqui que começa a diversão. Vamos ver como o Django Channels permite usar esses recursos em tempo real de forma eficiente e integrada.

Entendendo a Arquitetura do Django Channels

Antes de colocarmos a mão na massa com código, precisamos entender o básico da arquitetura do Django Channels. Tradicionalmente, o Django trabalha com o WSGI (Web Server Gateway Interface), que serve para lidar com requisições HTTP.

O WSGI é ótimo para requisições síncronas e baseadas em requisição/resposta, mas não foi projetado para lidar com coisas como WebSockets ou tarefas assíncronas. Para resolver esse problema, o ASGI (Asynchronous Server Gateway Interface) foi introduzido.

O ASGI é uma evolução do WSGI. Ele permite que aplicações Django lidem com requisições assíncronas, além de suportar outros tipos de protocolos, como WebSockets. Quando você usa o Django Channels, está usando o ASGI em vez do tradicional WSGI.

Por que o Django precisou do ASGI?

Quando o Django foi lançado, a web era basicamente síncrona. Cada requisição era independente, e o servidor respondia com os dados necessários. Agora, com a popularização de aplicativos em tempo real, como chats e notificações instantâneas, o Django precisava evoluir para lidar com essa nova demanda.

O ASGI permitiu que o Django expandisse suas capacidades para lidar com comunicação bidirecional e tarefas que exigem que o servidor fique “escutando” o cliente de forma contínua.

Então, se você quer que seu projeto Django lide com WebSockets, tarefas em segundo plano, ou qualquer outro recurso que precise de comunicação contínua entre o cliente e o servidor, o ASGI e o Django Channels são as ferramentas certas.

Instalando e Configurando Django Channels

Agora que entendemos o porquê de usar o Django Channels, vamos instalar e configurar o ambiente necessário para começar a utilizá-lo.

Passo 1: Instalando o Django Channels

A primeira coisa que você precisa fazer é instalar o pacote channels. Você pode fazer isso com o pip:

pip install channels
Python

Passo 2: Configurando o ASGI

Depois de instalar o Django Channels, precisamos configurar o Django para usar o ASGI em vez do WSGI. No arquivo settings.py, adicione channels na lista de aplicativos instalados:

INSTALLED_APPS = [
    # Outros apps do Django...
    'channels',
]
Python

Em seguida, adicione a configuração do ASGI:

ASGI_APPLICATION = 'meuprojeto.asgi.application'
Python

O Django agora espera um arquivo asgi.py, similar ao tradicional wsgi.py, para lidar com as conexões assíncronas. Crie o arquivo asgi.py na raiz do seu projeto, com o seguinte conteúdo básico:

import os
from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'meuprojeto.settings')

application = get_asgi_application()
Python

Agora estamos prontos para começar a trabalhar com WebSockets!

Implementando WebSockets no Django

O Django Channels facilita o uso de WebSockets criando um conceito de consumers. Um consumer é, basicamente, o equivalente a uma view, mas para WebSockets.

Passo 1: Criando um Consumer WebSocket

Vamos começar criando um consumidor WebSocket básico. Crie um arquivo chamado consumers.py no seu aplicativo Django:

import json
from channels.generic.websocket import WebsocketConsumer

class MeuConsumer(WebsocketConsumer):
    def connect(self):
        # Aceita a conexão WebSocket
        self.accept()

    def disconnect(self, close_code):
        # Chamado quando a conexão é fechada
        pass

    def receive(self, text_data):
        # Chamado quando uma mensagem é recebida
        text_data_json = json.loads(text_data)
        mensagem = text_data_json['mensagem']

        # Envia uma resposta de volta para o cliente
        self.send(text_data=json.dumps({
            'mensagem': mensagem
        }))
Python

Aqui temos um consumer que aceita a conexão WebSocket, recebe uma mensagem do cliente e, em seguida, devolve essa mensagem para o cliente. É uma implementação simples, mas serve como base para aplicações mais complexas.

Passo 2: Definindo Rotas para WebSockets

Agora que temos nosso consumer, precisamos definir as rotas (ou URLs) para os WebSockets. Em vez de usar o arquivo urls.py, como fazemos com views tradicionais, as rotas dos WebSockets são definidas no routing.py.

Crie um arquivo chamado routing.py e adicione o seguinte código:

from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
    re_path(r'ws/chat/$', consumers.MeuConsumer.as_asgi()),
]
Python

Aqui, estamos mapeando a URL ws/chat/ para o nosso consumidor WebSocket. Note que usamos as_asgi() para garantir que o consumidor seja executado de forma assíncrona.

Passo 3: Integrando com o ASGI

No arquivo asgi.py, precisamos adicionar a configuração de roteamento para WebSockets. Atualize o arquivo para incluir a seguinte configuração:

import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from meuapp.routing import websocket_urlpatterns

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'meuprojeto.settings')

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AuthMiddlewareStack(
        URLRouter(
            websocket_urlpatterns
        )
    ),
})
Python

Aqui estamos configurando o ASGI para lidar com requisições HTTP tradicionais e também com WebSockets. O AuthMiddlewareStack garante que o Django ainda pode gerenciar autenticação e sessões com WebSockets.

Lidando com Recursos Assíncronos no Django Channels

Um dos grandes benefícios do Django Channels é que ele permite trabalhar com tarefas assíncronas, permitindo que o servidor lide com várias requisições de forma mais eficiente.

Passo 1: Criando um Consumer Assíncrono

Assim como as views, os consumers também podem ser assíncronos. Para criar um consumer assíncrono, basta modificar o código para usar AsyncWebsocketConsumer em vez de WebsocketConsumer:

from channels.generic.websocket import AsyncWebsocketConsumer
import json

class MeuConsumerAssincrono(AsyncWebsocketConsumer):
    async def connect(self):
        await self.accept()

    async def disconnect(self, close_code):
        pass

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        mensagem = text_data_json['mensagem']

        await self.send(text_data=json.dumps({
            'mensagem': mensagem
        }))
Python

Agora, nosso consumer é assíncrono, o que significa que ele pode lidar com várias requisições simultâneas de forma mais eficiente.

Exemplo Prático de Aplicação em Tempo Real

Vamos implementar um chat em tempo real como exemplo prático. Esse exemplo envolve múltiplos usuários conectados ao mesmo canal, trocando mensagens em tempo real.

  1. Atualize o consumer para lidar com grupos:
from channels.generic.websocket import AsyncWebsocketConsumer
import json

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = f'chat_{self.room_name}'

        # Entra no grupo
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

    async def disconnect(self, close_code):
        # Sai do grupo
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        mensagem = text_data_json['mensagem']

        # Envia a mensagem para o grupo
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'mensagem': mensagem
            }
        )

    async def chat_message(self, event):
        mensagem = event['mensagem']

        # Envia a mensagem para WebSocket
        await self.send(text_data=json.dumps({
            'mensagem': mensagem
        }))
Python

Aqui, estamos criando um consumidor que adiciona os usuários a grupos e permite o envio de mensagens para todos os usuários conectados ao grupo.

Integração com Redis para Maior Escalabilidade

Se você deseja que sua aplicação em tempo real seja escalável, é recomendado usar o Redis como backend para o Django Channels. O Redis permite gerenciar várias instâncias de servidor compartilhando o mesmo canal de comunicação.

Passo 1: Instalando o Redis

Primeiro, instale o Redis no servidor e o cliente Redis para Python:

sudo apt-get install redis-server
pip install channels-redis
Python

Passo 2: Configurando Redis no Django

Agora, no arquivo settings.py, configure o Redis como o Channel Layer:

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}
Python

Com isso, o Redis gerenciará os grupos e as mensagens, permitindo que múltiplos servidores compartilhem o mesmo canal WebSocket.

Autenticação e Segurança com WebSockets

Agora que já temos WebSockets funcionando, um aspecto importante é garantir que eles sejam seguros e que apenas usuários autenticados possam acessar determinados canais.

Autenticação em WebSockets

O Django Channels facilita o uso de autenticação em WebSockets através do AuthMiddlewareStack, que já foi incluído na nossa configuração do ASGI. Isso garante que, se o usuário estiver logado, a autenticação será passada automaticamente para o WebSocket.

No consumer, podemos acessar o usuário autenticado assim:

self.scope["user"]
Python

Isso permite que você implemente lógicas baseadas em permissões para os usuários autenticados.

Melhorando a Segurança

  • Verificação de origem: Sempre valide as origens (domínios) que podem abrir WebSockets para evitar ataques de Cross-Site WebSocket Hijacking.
  • Autorização: Certifique-se de que apenas usuários autorizados possam enviar mensagens para determinados canais.
  • Rate Limiting: Implementar limitação de taxa para evitar sobrecarga causada por usuários mal-intencionados.

Melhores Práticas e Considerações Finais

O Django Channels é uma ferramenta poderosa que permite que você adicione funcionalidades em tempo real a aplicações Django tradicionais. No entanto, é importante usar essa tecnologia com cautela, para evitar problemas de desempenho e segurança.

Aqui estão algumas melhores práticas:

  • Use Redis para escalabilidade: Se sua aplicação vai lidar com muitos usuários simultâneos, o Redis é essencial para garantir que os WebSockets funcionem de forma eficiente.
  • Teste a performance: Verifique se sua aplicação está escalando corretamente à medida que mais usuários se conectam.
  • Implemente segurança: Nunca exponha WebSockets sem autenticação ou autorização adequada. Garanta que apenas usuários autenticados possam enviar e receber mensagens.

Conclusão

O Django Channels oferece uma forma eficiente e escalável de adicionar funcionalidades em tempo real às suas aplicações Django. Com WebSockets, tarefas assíncronas e a capacidade de lidar com múltiplos usuários ao mesmo tempo, o Django Channels transforma o Django tradicional em uma plataforma muito mais dinâmica e interativa.

Ao seguir este guia, você já deve ter uma boa base para começar a implementar WebSockets e recursos assíncronos no seu projeto. Lembre-se de sempre considerar a escalabilidade e a segurança, especialmente quando estiver lidando com um grande número de conexões em tempo real.

Agora que você já sabe como configurar e usar Django Channels, é hora de implementar e explorar tudo o que essa poderosa ferramenta tem a oferecer. Afinal, em um mundo onde tudo acontece em tempo real, quem quer ficar preso em requisições HTTP tradicionais?