O Python Tornado é um framework web e uma biblioteca de rede assíncrona, projetada para resolver o problema C10k (gerenciar dez mil conexões simultâneas). Diferente de frameworks WSGI tradicionais como Django ou Flask, que utilizam um modelo síncrono, o Tornado emprega um loop de E/S (I/O) não bloqueante. Isso permite que o servidor processe milhares de conexões abertas simultaneamente com uma sobrecarga de memória mínima, tornando-o ideal para aplicações que exigem longa latência ou comunicação em tempo real.
Características Técnicas Principais
- Modelo de E/S Não Bloqueante: O núcleo do Tornado utiliza um epoll em Linux ou kqueue em macOS/BSD. Isso permite que um único thread gerencie múltiplas conexões de rede sem ficar aguardando a conclusão de cada operação de leitura ou escrita.
- Design Modular e Leve: O framework não impõe uma estrutura rígida de diretórios ou dependências pesadas. Os desenvolvedores podem escolher utilizar apenas os componentes web, o servidor assíncrono ou as bibliotecas de clientes HTTP separadamente.
- Suporte Nativo a WebSocket: O Tornado implementa o protocolo WebSocket de forma nativa, facilitando a criação de canais de comunicação bidirecionais full-duplex sem a necessidade de bibliotecas de terceiros para gerenciar o handshake e a manutenção da conexão.
- Corrotinas e Async/Await: O framework suporta nativamente a sintaxe
asynceawaitdo Python, além de decoradores comogen.coroutine, permitindo escrever código assíncrono que é estruturralmente similar ao código síncrono, simplificando a lógica de callbacks complexos.
Cenários de Aplicação
- Sistemas de Alta Concorrência: Serviços que precisam manter conexões persistentes com muitos usuários, como redes sociais ou plataformas de mensagens, se beneficiam da eficiência do Tornado em geranciar o ciclo de vida dessas conexões.
- Aplicações em Tempo Real: Devido ao suporte robusto a WebSockets, o framework é frequentemente escolhido para dashboards de administração ao vivo, jogos multiplayer baseados em navegador e ferramentas de colaboração simultânea.
- Integração com Serviços Lentos: Ao realizar requisições a APIs externas que podem ter latência variável, o modelo assíncrono evita que o thread do servidor fique bloqueado, permitindo que ele continue atendendo outras requisições enquanto espera a resposta.
Implementação Básica
Para iniciar o desenvolvimento, é necessário instalar a biblioteca via pip:
pip install tornado
Abaixo está um exemplo de uma aplicação mínima configurada para responder a requisições HTTP. O código utiliza uma porta diferente da padrão e renomeia os manipuladores para maior clareza.
import tornado.ioloop
import tornado.web
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write("Sistema Tornado Operacional")
def iniciar_servidor():
aplicacao = tornado.web.Application([
(r"/", IndexHandler),
])
aplicacao.listen(8080)
tornado.ioloop.IOLoop.current().start()
if __name__ == "__main__":
iniciar_servidor()
Definição de Rotas e Captura de Parâmetros
O sistema de roteamento do Tornado permite expressões regulares para capturar dinamicamente segmentos da URL. O exemplo a seguir demonstra como configurar rotas para um usuário e um identificador numérico.
import tornado.ioloop
import tornado.web
class HomeHandler(tornado.web.RequestHandler):
def get(self):
self.write("Página Inicial")
class PerfilHandler(tornado.web.RequestHandler):
def get(self, usuario_id):
self.write(f"Acessando dados do usuário ID: {usuario_id}")
def configurar_rotas():
return tornado.web.Application([
(r"/", HomeHandler),
(r"/perfil/([0-9]+)", PerfilHandler),
])
if __name__ == "__main__":
app = configurar_rotas()
app.listen(3000)
tornado.ioloop.IOLoop.current().start()
Processamento de Requisições Assíncronas
Para operações que envolvem latência de rede, como consultas a APIs externas, o Tornado oferece clientes HTTP assíncronos. O código abaixo ilustra uma requisição não bloqueante utilizando um cliente reutilizável.
import tornado.ioloop
import tornado.web
import tornado.gen
import tornado.httpclient
class DadosExternoHandler(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self):
cliente = self.obter_cliente()
try:
resposta = yield cliente.fetch("https://jsonplaceholder.typicode.com/todos/1")
self.write(resposta.body)
except Exception as erro:
self.set_status(500)
self.write(f"Falha na requisição: {erro}")
def obter_cliente(self):
if not hasattr(self, '_client_instancia'):
self._client_instancia = tornado.httpclient.AsyncHTTPClient()
return self._client_instancia
def criar_app():
return tornado.web.Application([
(r"/api/dados", DadosExternoHandler),
])
if __name__ == "__main__":
app = criar_app()
app.listen(5050)
tornado.ioloop.IOLoop.current().start()