AI Engineering Academy · Lezione

Bilanciamento del carico e strategie multi-key

Implementi il bilanciamento del carico round-robin e ponderato tra più chiavi API e account per ampliare il margine sui rate limit e ridurre i picchi di latenza p99.

Lezione 2 di 413 passaggi

Bilanciamento del carico e strategie multi-key è una lezione AI Engineering Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Engineering Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Engineering Academy include 4 lezioni in totale.

Perché una sola chiave API non è sufficiente

Una singola chiave API di OpenAI ha un limite di frequenza fisso, misurato in richieste al minuto (RPM) e token al minuto (TPM). Al Tier 1, GPT-4o consente 500 RPM e 30.000 TPM. Per un'applicazione in produzione con centinaia di utenti simultanei, una singola chiave raggiungerà costantemente questi limiti. Più chiavi API aumentano proporzionalmente il margine di capacità disponibile.

Creazione di più chiavi API

Potete creare più chiavi API all'interno di una singola organizzazione OpenAI oppure creare più account OpenAI, ciascuno con fatturazione separata. Salvate ogni chiave nella configurazione dell'ambiente e trattatele come un pool. Conservate le chiavi in un gestore dei segreti come AWS Secrets Manager o HashiCorp Vault, anziché nel codice sorgente o nei file .env inclusi nel controllo versione.

import os

API_KEYS = [
    os.environ['OPENAI_KEY_1'],
    os.environ['OPENAI_KEY_2'],
    os.environ['OPENAI_KEY_3'],
    os.environ['OPENAI_KEY_4'],
]

# Total effective RPM = 500 * 4 = 2000 RPM
# Total effective TPM = 30000 * 4 = 120000 TPM

Bilanciamento del carico round-robin

Il round-robin distribuisce uniformemente le richieste tra tutte le chiavi, selezionandole ciclicamente nell'ordine. È semplice da implementare e garantisce che ogni chiave gestisca all'incirca lo stesso carico nel tempo. Usate un contatore thread-safe o un intero atomico per evitare che due richieste simultanee selezionino la stessa chiave. Il round-robin funziona bene quando tutte le chiavi hanno limiti di frequenza identici.

import itertools
import threading
from openai import OpenAI

class RoundRobinPool:
    def __init__(self, keys: list):
        self._clients = [OpenAI(api_key=k) for k in keys]
        self._cycle = itertools.cycle(range(len(self._clients)))
        self._lock = threading.Lock()

    def get_client(self) -> OpenAI:
        with self._lock:
            idx = next(self._cycle)
        return self._clients[idx]

pool = RoundRobinPool(API_KEYS)
client = pool.get_client()

Bilanciamento del carico ponderato

Il bilanciamento del carico ponderato assegna alle chiavi di livello superiore, con limiti di frequenza più elevati, una quota maggiore di traffico proporzionale alla loro capacità. Se la chiave A è Tier 3 (10.000 RPM) e la chiave B è Tier 1 (500 RPM), la chiave A dovrebbe ricevere circa il 95% delle richieste. Il bilanciamento ponderato impedisce che le chiavi di livello inferiore diventino colli di bottiglia quando vengono utilizzate insieme a chiavi di livello superiore.

import random

class WeightedPool:
    def __init__(self, key_configs: list):
        # key_configs = [{'key': '...', 'weight': 10}, ...]
        self._clients = [OpenAI(api_key=c['key']) for c in key_configs]
        self._weights = [c['weight'] for c in key_configs]

    def get_client(self) -> OpenAI:
        return random.choices(self._clients, weights=self._weights, k=1)[0]

pool = WeightedPool([
    {'key': os.environ['OPENAI_KEY_TIER3'], 'weight': 20},
    {'key': os.environ['OPENAI_KEY_TIER1'], 'weight': 1},
])

Monitoraggio dello stato dei limiti di frequenza per chiave

L'API OpenAI restituisce intestazioni relative ai limiti di frequenza con ogni risposta: x-ratelimit-remaining-requests e x-ratelimit-remaining-tokens. Monitorate queste intestazioni per ogni chiave per sapere quali sono vicine all'esaurimento. Quando una chiave indica meno di 10 richieste rimanenti nel minuto corrente, instradate temporaneamente il traffico altrove per evitare errori 429 prima che si verifichino.

class SmartPool:
    def __init__(self, keys: list):
        self._clients = [OpenAI(api_key=k) for k in keys]
        self._remaining = {i: 500 for i in range(len(keys))}  # initial RPM

    def get_best_client(self):
        # Pick key with most remaining capacity
        best_idx = max(self._remaining, key=lambda i: self._remaining[i])
        return self._clients[best_idx], best_idx

    def update_remaining(self, idx: int, response_headers: dict):
        remaining = int(response_headers.get('x-ratelimit-remaining-requests', 0))
        self._remaining[idx] = remaining

Gestione degli errori 429 relativi ai limiti di frequenza

Quando una chiave restituisce un errore 429, rimuovetela immediatamente dal pool per la durata indicata nell'intestazione Retry-After (in genere 60 secondi). Contrassegnatela come in raffreddamento e instradate tutto il traffico verso le chiavi rimanenti. Al termine dell'intervallo di raffreddamento, reinserite la chiave nel pool. Questo previene i guasti a cascata, in cui i tentativi ripetuti sulla stessa chiave peggiorano la situazione.

import time
from openai import RateLimitError

class CooldownPool:
    def __init__(self, keys: list):
        self._clients = [(OpenAI(api_key=k), None) for k in keys]  # (client, cooldown_until)

    def get_available_clients(self):
        now = time.time()
        return [
            (i, c) for i, (c, until) in enumerate(self._clients)
            if until is None or until <= now
        ]

    def mark_cooling(self, idx: int, retry_after: int = 60):
        client, _ = self._clients[idx]
        self._clients[idx] = (client, time.time() + retry_after)
        print(f'Key {idx} cooling down for {retry_after}s')

Utilizzo di OpenRouter come multiplexer

OpenRouter è un servizio proxy che espone centinaia di modelli attraverso un singolo endpoint API compatibile con OpenAI. Instradando le richieste tramite OpenRouter, ottenete automaticamente il bilanciamento del carico tra più account dei provider sottostanti, il fallback verso provider alternativi e l'accesso a modelli open source come riserva. Il sovrapprezzo è contenuto rispetto alla semplicità operativa offerta.

from openai import OpenAI

# OpenRouter uses the same OpenAI SDK interface
client = OpenAI(
    api_key=os.environ['OPENROUTER_API_KEY'],
    base_url='https://openrouter.ai/api/v1'
)

response = client.chat.completions.create(
    model='openai/gpt-4o',  # OpenRouter model name format
    messages=[{'role': 'user', 'content': prompt}]
)
# Automatic failover if OpenAI is down

Monitoraggio dello stato delle chiavi con le metriche

Monitorate per ogni chiave metriche come le richieste inviate, gli errori 429 ricevuti e il tempo di raffreddamento nell'ultima ora. Una chiave con un'elevata frequenza di errori 429 richiede una riduzione del traffico oppure un aggiornamento del livello. Esponete queste metriche in formato Prometheus su un endpoint /metrics, in modo che il sistema di monitoraggio possa generare un avviso quando una chiave raggiunge costantemente i limiti.

from dataclasses import dataclass, field
from collections import defaultdict

@dataclass
class KeyMetrics:
    requests_sent: int = 0
    rate_limit_errors: int = 0
    total_tokens_used: int = 0
    cooldown_count: int = 0

class MetricPool:
    def __init__(self, keys: list):
        self._clients = [OpenAI(api_key=k) for k in keys]
        self._metrics = [KeyMetrics() for _ in keys]

    def report(self):
        for i, m in enumerate(self._metrics):
            error_rate = m.rate_limit_errors / max(m.requests_sent, 1)
            print(f'Key {i}: {m.requests_sent} req, {error_rate:.1%} 429 rate')

Distribuzione geografica delle chiavi

Se i vostri utenti sono distribuiti in tutto il mondo, valutate la possibilità di mantenere chiavi API separate per area geografica e di instradare le richieste verso la chiave più vicina all'utente. Ridurre il tempo di andata e ritorno della rete migliora il TTFT. Distribuite in ogni area un bilanciatore del carico leggero (AWS Lambda@Edge o Cloudflare Worker) che selezioni la chiave appropriata e inoltri la richiesta, impedendo che le chiavi siano esposte ai client.

REGIONAL_KEYS = {
    'us-east': os.environ['OPENAI_KEY_US_EAST'],
    'eu-west': os.environ['OPENAI_KEY_EU_WEST'],
    'ap-southeast': os.environ['OPENAI_KEY_AP'],
}

def get_key_for_region(user_region: str) -> str:
    # Default to us-east if region unknown
    return REGIONAL_KEYS.get(user_region, REGIONAL_KEYS['us-east'])

Test del bilanciatore del carico

Scrivete un test di carico che invii 100 richieste simultanee attraverso il pool di bilanciamento e misuri la distribuzione, i tassi di errore e i percentili di latenza. Verificate che nessuna singola chiave gestisca una quota superiore a quella proporzionale e che gli errori 429 siano inferiori allo 0,1%. Usate asyncio.gather o uno strumento come Locust per simulare il carico simultaneo che il sistema di produzione dovrà effettivamente gestire.

import asyncio
import time

async def load_test(pool, concurrency=100, total=1000):
    sem = asyncio.Semaphore(concurrency)
    results = []

    async def one_request():
        async with sem:
            client = pool.get_client()
            start = time.perf_counter()
            try:
                await client.chat.completions.create(
                    model='gpt-4o-mini',
                    messages=[{'role': 'user', 'content': 'Ping'}],
                    max_tokens=5
                )
                results.append(('ok', time.perf_counter() - start))
            except Exception as e:
                results.append(('error', str(e)))

    await asyncio.gather(*[one_request() for _ in range(total)])
    ok = [r for r in results if r[0] == 'ok']
    print(f'Success rate: {len(ok)/total:.1%}')
    return results

Scelta della strategia di bilanciamento corretta

Adattate la strategia di bilanciamento alla struttura dei vostri limiti di frequenza. Usate il round-robin quando tutte le chiavi hanno limiti di livello identici e il traffico è distribuito uniformemente. Usate il bilanciamento ponderato quando le chiavi hanno limiti di livello diversi. Usate l'instradamento basato sullo stato di salute (ignorando le chiavi vicine all'esaurimento) quando dovete ridurre al minimo gli errori 429 in presenza di picchi di traffico. Per la maggior parte dei sistemi di produzione, l'instradamento basato sullo stato di salute con backoff esponenziale offre il miglior equilibrio tra semplicità e resilienza.

# Strategy selection guide:
# Scenario A: 4 keys all Tier 2 (same limits)
#   -> Round-robin: simple, even distribution
#
# Scenario B: 1 Tier 3 key + 3 Tier 1 keys
#   -> Weighted: Tier 3 gets 10x weight
#
# Scenario C: Variable traffic with burst periods
#   -> Health-aware: track remaining headers, skip near-limit keys
#
# Scenario D: Multi-region, latency-sensitive
#   -> Geographic: regional keys, route by user location

Verifica rapida

Verificate la vostra comprensione delle strategie di bilanciamento del carico per le API degli LLM.

Riepilogo della lezione

In questa lezione avete imparato che il bilanciamento round-robin e ponderato distribuisce il traffico tra più chiavi API, aumentando il margine rispetto ai limiti di frequenza, il monitoraggio del raffreddamento previene gli errori 429 a cascata rimuovendo temporaneamente le chiavi soggette a limitazione, e OpenRouter offre un'opzione di multiplexing gestita con fallback automatico. Ora implementeremo i provider di fallback e i circuit breaker.

Gratis per iniziare

Impara Python con un tutor IA — gratis

Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.

Corsi
30
Lezioni
120

Domande Frequenti

La lezione «Bilanciamento del carico e strategie multi-key» è gratuita?

Sì — il testo completo di «Bilanciamento del carico e strategie multi-key» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Engineering Academy, passa a CoddyKit PRO. Il corso AI Engineering Academy include 4 lezioni in totale.

Cosa imparerò in «Bilanciamento del carico e strategie multi-key»?

Implementi il bilanciamento del carico round-robin e ponderato tra più chiavi API e account per ampliare il margine sui rate limit e ridurre i picchi di latenza p99. Eserciti AI Engineering Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare AI Engineering Academy?

Non è richiesta alcuna esperienza precedente. AI Engineering Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Bilanciamento del carico e strategie multi-key»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione AI Engineering Academy?

Sì. Ogni lezione AI Engineering Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Misurare la latenza degli LLM: TTFT e TPOT
  2. Bilanciamento del carico e strategie multi-key
  3. Provider di fallback e circuit breaker
  4. Budget di timeout e degrado controllato
← Torna a AI Engineering Academy