Python Hypercorn: ASGI serveris

Kas yra Hypercorn ir kam jo reikia?

Jei dirbi su Python web aplikacijomis, tikriausiai jau girdėjai apie ASGI – Asynchronous Server Gateway Interface. Tai modernesnė WSGI alternatyva, kuri leidžia kurti asinchronines aplikacijas ir išnaudoti visas šiuolaikinių Python galimybes. O Hypercorn yra vienas iš galingiausių ASGI serverių, kuris puikiai tinka tiek kūrimui, tiek produkcijai.

Hypercorn atsirado kaip Quart framework dalis, bet greitai išaugo į savarankišką projektą. Jis palaiko HTTP/1, HTTP/2, HTTP/3 (per QUIC), WebSockets ir net gali dirbti su WSGI aplikacijomis. Tai reiškia, kad vienas serveris gali aptarnauti įvairius protokolus ir aplikacijų tipus.

Dažnai klausiama – kodėl ne Uvicorn? Uvicorn yra puikus, greitas ir paprastas. Bet Hypercorn siūlo daugiau funkcionalumo iš dėžės: HTTP/2 palaikymą be papildomų konfigūracijų, geresnes debugging galimybes ir lankstesnį konfigūravimą. Jei tau reikia tik paprasto HTTP/1.1 serverio – Uvicorn puikiai tiks. Bet kai projektas auga ir reikia daugiau galimybių, Hypercorn tampa logiška evoliucija.

Įdiegimas ir pirmieji žingsniai

Pradėti su Hypercorn tikrai nesudėtinga. Paprasčiausias būdas – per pip:

pip install hypercorn

Jei planuoji naudoti HTTP/3, reikės papildomų priklausomybių:

pip install hypercorn[h3]

Dabar sukurkime paprasčiausią ASGI aplikaciją. Sukurk failą app.py:


async def app(scope, receive, send):
if scope['type'] == 'http':
await send({
'type': 'http.response.start',
'status': 200,
'headers': [[b'content-type', b'text/plain']],
})
await send({
'type': 'http.response.body',
'body': b'Labas is Hypercorn!',
})

Paleisti serverį galima viena komanda:

hypercorn app:app

Ir viskas – serveris veikia ant localhost:8000. Žinoma, tokia žemo lygio ASGI aplikacija retai kada naudojama praktikoje. Dažniausiai dirbi su framework’ais kaip FastAPI, Quart ar Starlette.

Darbas su populiariais framework’ais

FastAPI su Hypercorn veikia itin sklandžiai. Štai pavyzdys su FastAPI aplikacija:


from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "FastAPI su Hypercorn"}

@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}

Paleidi taip:

hypercorn main:app --bind 0.0.0.0:8000

Jei naudoji Quart (kuris, beje, buvo sukurtas Hypercorn kūrėjo), integracija dar sklandesnė:


from quart import Quart, websocket

app = Quart(__name__)

@app.route('/')
async def hello():
return 'Labas iš Quart!'

@app.websocket('/ws')
async def ws():
while True:
data = await websocket.receive()
await websocket.send(f"Echo: {data}")

Vienas iš Hypercorn privalumų – puikus WebSocket palaikymas. Skirtingai nuo kai kurių kitų serverių, čia WebSocket’ai veikia be jokių papildomų triukų ar konfigūracijų.

Konfigūravimas produkcijai

Kai aplikacija eina į produkciją, reikia rimtesnės konfigūracijos. Hypercorn siūlo du būdus: komandinės eilutės argumentai arba konfigūracijos failas.

Komandinėje eilutėje gali nurodyti workers skaičių, bind adresą, log lygį ir daug daugiau:


hypercorn app:app \
--bind 0.0.0.0:8000 \
--workers 4 \
--access-logfile - \
--error-logfile - \
--log-level info

Bet patogiau naudoti konfigūracijos failą. Sukurk hypercorn_config.py:


import multiprocessing

bind = ["0.0.0.0:8000"]
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "asyncio"
accesslog = "-"
errorlog = "-"
loglevel = "info"
keepalive = 120
graceful_timeout = 30

Tada paleidi su:

hypercorn app:app -c hypercorn_config.py

Dėl workers skaičiaus – klasikinė formulė CPU_COUNT * 2 + 1 dažnai veikia gerai, bet reikia testuoti su savo konkrečia aplikacija. Jei turi daug I/O operacijų, gali padidinti. Jei daugiau CPU intensyvių operacijų – mažinti.

HTTP/2 ir HTTP/3 galimybės

Viena iš Hypercorn stipriųjų pusių – natūralus HTTP/2 palaikymas. Daugelis kitų Python serverių reikalauja papildomų proxy ar konfigūracijų, bet Hypercorn tiesiog veikia.

HTTP/2 įjungti labai paprasta – tereikia SSL sertifikatų:


hypercorn app:app \
--bind 0.0.0.0:443 \
--certfile cert.pem \
--keyfile key.pem

Hypercorn automatiškai derasi su klientu ir naudoja HTTP/2, jei klientas jį palaiko. Jokių papildomų nustatymų nereikia.

HTTP/3 (per QUIC protokolą) yra dar naujesnis dalykas. Jis veikia per UDP ir siūlo greitesnį prisijungimą bei geresnį veikimą nestabiliuose tinkluose. Įjungti HTTP/3:


pip install hypercorn[h3]

hypercorn app:app \
--bind 0.0.0.0:443 \
--quic-bind 0.0.0.0:443 \
--certfile cert.pem \
--keyfile key.pem

Reikia suprasti, kad HTTP/3 vis dar nėra visur palaikomas. Naršyklės palaiko, bet daug įrankių ir bibliotekų dar ne. Tai labiau „ateities investicija” nei būtinybė dabar.

Performance tuning ir optimizacijos

Kai aplikacija pradeda gauti rimtą traffic’ą, reikia pagalvoti apie optimizacijas. Štai keletas praktinių patarimų:

Workers vs threads – Hypercorn palaiko tiek process-based, tiek thread-based workers. Asyncio aplikacijoms process-based workers dažniausiai geresnis pasirinkimas, nes Python GIL netrukdo. Bet jei turi daug shared state, thread-based gali būti efektyvesnis.

Backlog dydis – tai kiek connection’ų gali laukti eilėje. Default’as dažnai per mažas produkcijai:


backlog = 2048

Keep-alive timeout – per ilgas keep-alive naudoja resursus, per trumpas didina latency. Rask balansą:


keepalive = 75 # sekundės

Graceful shutdown – kai restartuoji serverį, leisk esamiems request’ams užsibaigti:


graceful_timeout = 30

Vienas dažnai pamirštamas dalykas – file descriptor limits. Linux sistemose default limitas gali būti per mažas. Patikrink su ulimit -n ir padidink jei reikia:


ulimit -n 65536

Arba nustatyk /etc/security/limits.conf failą sisteminiams pakeitimams.

Logging ir monitoring

Produkcinėje sistemoje be gero logging’o niekur. Hypercorn palaiko kelis logging formatus ir galimybes.

Structured logging su JSON formatu labai naudingas, kai naudoji log agregavimo įrankius:


import logging
from pythonjsonlogger import jsonlogger

logHandler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter()
logHandler.setFormatter(formatter)
logger = logging.getLogger()
logger.addHandler(logHandler)
logger.setLevel(logging.INFO)

Access log’ai gali būti konfigūruojami su custom formatu:


accesslog = "-" # stdout
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'

Dėl monitoring’o – Hypercorn puikiai veikia su Prometheus. Gali naudoti middleware, kuris eksportuoja metricas:


from prometheus_client import Counter, Histogram
from starlette.middleware.base import BaseHTTPMiddleware

REQUEST_COUNT = Counter('http_requests_total', 'Total HTTP requests', ['method', 'endpoint'])
REQUEST_DURATION = Histogram('http_request_duration_seconds', 'HTTP request duration')

Svarbu monitorinti ne tik aplikacijos metricas, bet ir paties Hypercorn būseną – worker’ių skaičių, memory usage, connection pool’us.

Deployment strategijos ir best practices

Kai ateina laikas dėti aplikaciją į produkciją, yra keletas įrodytų strategijų.

Su Docker – Dockerfile pavyzdys:


FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["hypercorn", "app:app", "--bind", "0.0.0.0:8000", "--workers", "4"]

Su systemd – sukurk service failą /etc/systemd/system/myapp.service:


[Unit]
Description=My Hypercorn Application
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
Environment="PATH=/opt/myapp/venv/bin"
ExecStart=/opt/myapp/venv/bin/hypercorn app:app -c hypercorn_config.py
Restart=always

[Install]
WantedBy=multi-user.target

Su Nginx reverse proxy – nors Hypercorn gali veikti tiesiogiai, dažnai naudojamas Nginx priekyje SSL termination, static files ir load balancing:


upstream hypercorn {
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
}

server {
listen 80;
server_name example.com;

location / {
proxy_pass http://hypercorn;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

Svarbu nepamiršti WebSocket konfigūracijos, jei juos naudoji:


location /ws {
proxy_pass http://hypercorn;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

Ką daryti kai kyla problemų

Debugging ASGI aplikacijų kartais būna sudėtingas. Štai keletas dažniausių problemų ir sprendimų.

Memory leaks – jei pastebėjai, kad memory auga ir neatlaisvinama, pirmas žingsnis – įjungti detalesnį logging’ą:


hypercorn app:app --log-level debug

Naudok tracemalloc modulį Python kode, kad rastum kur memory „dingsta”:


import tracemalloc
tracemalloc.start()

# Tavo kodas

snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
print(stat)

Lėtas response time – patikrink ar naudoji async/await teisingai. Viena dažniausių klaidų – naudoti blocking operacijas async funkcijose:


# Blogai
async def bad_endpoint():
result = requests.get('http://api.example.com') # Blocking!
return result.json()

# Gerai
async def good_endpoint():
async with httpx.AsyncClient() as client:
result = await client.get('http://api.example.com')
return result.json()

Connection timeouts – jei klientai gauna timeout’us, gali būti kad worker’ių per mažai arba jie užblokuoti. Padidink worker’ių skaičių arba patikrink ar nėra ilgai trunkančių operacijų.

Dar viena naudinga debugging technika – naudoti --reload flag’ą development’e, kad serveris automatiškai restartuotų po kodo pakeitimų:


hypercorn app:app --reload

Bet niekada nenaudok šito produkcijoje – tai lėtina ir gali sukelti problemų.

Kai Hypercorn tampa tavo patikimu partneriu

Po kelių mėnesių darbo su Hypercorn pradedi vertinti jo stabilumą ir funkcionalumą. Tai nėra pats greičiausias serveris (Uvicorn benchmark’uose dažnai laimi), bet tai patikimas, feature-rich sprendimas, kuris veikia.

Ypač vertinu HTTP/2 palaikymą be jokių triukų – tiesiog veikia. WebSocket’ai stabilūs net dideliame traffic’e. Konfigūracija lanksti, bet ne per daug sudėtinga. Dokumentacija galėtų būti geresnė, bet community aktyvus ir problemas sprendžia greitai.

Jei kuri naują projektą su FastAPI ar Quart, rekomenduočiau pradėti su Uvicorn development’e dėl paprastumo, bet produkcijai rimtai apsvarstyk Hypercorn. Jei žinai kad reikės HTTP/2, WebSocket’ų ar planuoji augti – Hypercorn nuo pat pradžių sutaupys laiko.

Svarbiausia – testuok su savo konkrečiu use case. Kiekviena aplikacija skirtinga, ir tai kas veikia vienam, nebūtinai optimalus kitam. Bet turint Hypercorn savo įrankių arsenale, turi stiprų sprendimą, kuris neapvils kai projektas pradės augti ir reikės daugiau galimybių nei paprastas HTTP/1.1 serveris gali pasiūlyti.

Daugiau

Open Props: CSS pasirinktinės savybės