AUTONETOPS

Cover Image for HTTPX: O Sucessor do Requests

HTTPX: O Sucessor do Requests

·

6 min read

A biblioteca requests tem sido indiscutivelmente a ferramenta das requisições HTTP em Python. Sua API tornou a interação com APIs uma tarefa trivial, e ela se tornou a base de incontáveis scripts de automação de redes. No entanto, o mundo da programação e das redes evoluiu. Aplicações modernas exigem alta concorrência e eficiência, e é aqui que o requests, com sua natureza puramente síncrona, começa a mostrar sua idade.

É nesse cenário que surge o HTTPX, uma biblioteca de cliente HTTP de nova geração para Python. O HTTPX oferece uma API amplamente compatível, o que torna a migração quase indolor, mas adiciona superpoderes que são cruciais para a automação de redes moderna: suporte nativo a async/await e HTTP/2

Vamos focar nas vantagens do httpx e mostrar por que ele deve se tornar a sua nova ferramenta padrão para interagir com APIs de rede.

Requests vs. HTTPX: Uma Comparação Direta

Característicarequestshttpx
APISíncronaSíncrona e Assíncrona
Protocolo HTTPHTTP/1.1HTTP/1.1 e HTTP/2
PerformanceExcelente para tarefas sequenciais.Superior para tarefas concorrentes (I/O-bound).
DependênciasNenhuma para funcionalidades básicas.httpcore, sniffio, anyio (para async).
CompatibilidadePadrão de fato, enorme ecossistema.API quase 100% compatível com requests.
UsoMenos ideal para frameworks web assíncronos.Ideal para FastAPI, Starlette…

Vantagem 1: Programação Assíncrona (async/await)

A maior limitação do requests é que ele é síncrono. Quando você faz uma requisição, seu código para e espera a resposta do servidor antes de continuar. Em automação de redes, isso é um gargalo fatal. Imagine precisar consultar a API de 100 switches para obter o status de suas interfaces. Com requests, você faria isso em série:

sequenceDiagram
    participant Script
    participant Switch1
    participant Switch2
    participant Switch100

    Script->>Switch1: GET /api/interfaces
    Note right of Script: Espera...
    Switch1-->>Script: Resposta
    Script->>Switch2: GET /api/interfaces
    Note right of Script: Espera...
    Switch2-->>Script: Resposta
    Note over Script: ...continua sequencialmente...
    Script->>Switch100: GET /api/interfaces
    Note right of Script: Espera...
    Switch100-->>Script: Resposta

Se cada requisição levar 1 segundo, o processo todo levará 100 segundos. A maior parte desse tempo é gasta esperando a rede e o servidor (I/O-bound).

O HTTPX com async resolve isso. Ele permite que você dispare todas as 100 requisições de uma vez e espere pelas respostas concorrentemente. Enquanto uma requisição está esperando pela rede, o programa pode trabalhar em outra.

sequenceDiagram
    participant Script
    participant Switch1
    participant Switch2
    participant Switch100

    Script->>Switch1: GET /api/interfaces
    Script->>Switch2: GET /api/interfaces
    Script->>Switch100: GET /api/interfaces
    Note right of Script: Dispara todas as requisições

    Switch1-->>Script: Resposta
    Switch2-->>Script: Resposta
    Switch100-->>Script: Resposta
    Note right of Script: Processa as respostas à medida que chegam

Com async, o tempo total será próximo ao da requisição mais longa, talvez pouco mais de 1 segundo, em vez de 100 segundos. Uma melhoria de performance de quase 100x.

Caso Prático: Consultando Múltiplos Dispositivos no NetBox

Vamos buscar os detalhes de 3 dispositivos diferentes no NetBox, primeiro com requests e depois com httpx.

Com requests (Sequencial):

import requests
import time

NETBOX_URL = "https://demo.netbox.dev/api"
DEVICE_IDS = [1, 2, 3]

def fetch_devices_sync():
    start_time = time.time()
    for device_id in DEVICE_IDS:
        response = requests.get(f"{NETBOX_URL}/dcim/devices/{device_id}/")
        print(f"Dispositivo {device_id}: {response.json()["name"]}")
    duration = time.time() - start_time
    print(f"\nVersão síncrona levou: {duration:.2f} segundos")

fetch_devices_sync()

Com httpx (Concorrente):

import httpx
import asyncio
import time

NETBOX_URL = "https://demo.netbox.dev/api"
DEVICE_IDS = [1, 2, 3]

async def fetch_device(client, device_id):
    response = await client.get(f"{NETBOX_URL}/dcim/devices/{device_id}/")
    print(f"Dispositivo {device_id}: {response.json()["name"]}")

async def fetch_devices_async():
    start_time = time.time()
    async with httpx.AsyncClient() as client:
        tasks = [fetch_device(client, device_id) for device_id in DEVICE_IDS]
        await asyncio.gather(*tasks)
    duration = time.time() - start_time
    print(f"\nVersão assíncrona levou: {duration:.2f} segundos")

asyncio.run(fetch_devices_async())

Embora para 3 dispositivos a diferença possa ser pequena, para 100 ou 1000, a versão assíncrona será ordens de magnitude mais rápida.

Vantagem 2: Suporte a HTTP/2

O HTTP/2 é uma revisão majoritária do protocolo HTTP que traz melhorias significativas de performance, especialmente o multiplexing. Com HTTP/1.1, se você precisa de 5 recursos de um mesmo servidor, o navegador (ou seu cliente HTTP) abre múltiplas conexões TCP, o que é custoso. Com HTTP/2, todas essas requisições podem ser enviadas concorrentemente sobre uma única conexão TCP

Para APIs modernas que são servidas sobre HTTP/2 (como muitas APIs gRPC-web e de provedores de nuvem), usar um cliente que suporte o protocolo pode reduzir a latência e o consumo de recursos.

Com httpx, habilitar HTTP/2 é trivial:

import httpx

# O cliente tentará negociar HTTP/2 com o servidor
with httpx.Client(http2=True) as client:
    response = client.get("https://http2.pro/api/v1")
    print(response.http_version)
    # Output: "HTTP/2"

A biblioteca requests não possui suporte nativo a HTTP/2.

Migrando de requests para httpx: Um Cheatsheet

A beleza do httpx é que sua API síncrona é um substituto quase direto para o requests. A migração é incrivelmente fácil.

requestshttpx (Síncrono)httpx (Assíncrono)
import requestsimport httpximport httpx & import asyncio
requests.get(url)httpx.get(url)await client.get(url)
requests.post(url, json=data)httpx.post(url, json=data)await client.post(url, json=data)
response.json()response.json()response.json()
response.status_coderesponse.status_coderesponse.status_code
response.textresponse.textresponse.text
requests.Session()httpx.Client()httpx.AsyncClient()
with requests.Session() as s:with httpx.Client() as c:async with httpx.AsyncClient() as c:

Passo 1: Migração Síncrona

Na maioria dos casos, você pode simplesmente substituir import requests por import httpx e seu código continuará funcionando.

Passo 2: Adotando o Async (Onde faz sentido)

Para as partes do seu código que fazem múltiplas chamadas de API, refatore-as para usar async def, httpx.AsyncClient, e asyncio.gather para obter os ganhos de performance.

Conclusão: Por que o HTTPX é o Futuro para NetDevOps

O requests é uma biblioteca fantástica e serviu à comunidade Python por muitos anos. Ela não está "morta" e ainda é perfeitamente adequada para scripts simples e sequenciais. No entanto, a automação de redes moderna é inerentemente concorrente. Precisamos interagir com dezenas, centenas ou milhares de dispositivos e APIs o mais rápido possível.

O HTTPX não é apenas uma alternativa; é a evolução natural. Ele nos dá:

  • Performance: A capacidade de executar operações de I/O em paralelo com async.

  • Eficiência: Suporte a HTTP/2 para interações mais rápidas com APIs modernas.

  • Flexibilidade: Uma única biblioteca que pode operar tanto de forma síncrona quanto assíncrona.

  • Familiaridade: Uma API que se sente em casa para qualquer um que já usou requests.

Para qualquer novo projeto de automação de redes que envolva interação com APIs, o httpx deveria ser sua escolha padrão. Comece usando sua API síncrona para uma transição fácil e, em seguida, adote o async nas partes do seu código que mais se beneficiarão da concorrência. Sua automação agradecerá pela velocidade.

Referências:

  1. HTTPX Official Documentation

  2. MDN Web Docs - HTTP/2

  3. ScrapingAnt - Requests vs. HTTPX - A Detailed Comparison

;