# Connection Pooling Patterns **Last updated:** 2026-06-19 | Verified: MCP 2026-06-19 **Status:** GA **Category:** Performance & Scalability **Type:** reference **Source:** https://learn.microsoft.com/azure/architecture/ai-ml/guide/azure-openai-gateway-multi-backend --- ## Innhold - [Introduksjon](#introduksjon) - [Kjernekomponenter](#kjernekomponenter) - [Pool Sizing-strategier](#pool-sizing-strategier) - [Keep-alive-konfigurasjon](#keep-alive-konfigurasjon) - [Connection Recycling](#connection-recycling) - [Load Distribution](#load-distribution) - [Azure API Management som Connection Pooling-lag (oppdatert 2026-06-19)](#azure-api-management-som-connection-pooling-lag-oppdatert-2026-06-19) - [Norsk offentlig sektor](#norsk-offentlig-sektor) - [Beslutningsrammeverk](#beslutningsrammeverk) - [Referanser](#referanser) - [For Cosmo](#for-cosmo) ## Introduksjon Connection pooling er en kritisk ytelsesoptimalisering for applikasjoner som kommuniserer med Azure AI Services. Hver HTTP-forbindelse til Azure OpenAI eller andre AI-endepunkter krever TCP-håndtrykk og eventuelt TLS-forhandling, noe som legger til betydelig latens per forespørsel. Uten connection pooling opprettes og lukkes forbindelser for hver eneste forespørsel, noe som fører til port-utmattelse, økt responstid og unødvendig CPU-bruk. I .NET-økosystemet er `HttpClient` den sentrale klassen for HTTP-kommunikasjon, og Microsofts offisielle retningslinjer er tydelige: bruk én statisk `HttpClient`-instans per logisk klient, eller bruk `IHttpClientFactory` for å håndtere DNS-endringer og connection lifecycle. Azure OpenAI SDK-ene (både Python og C#) håndterer connection pooling internt, men korrekt konfigurasjon er fortsatt nødvendig for optimal ytelse. For norsk offentlig sektor der AI-tjenester typisk nås via private endpoints og ofte går gjennom Azure API Management, er connection pooling spesielt viktig. Nettverkskjeden (klient → APIM → Private Endpoint → Azure OpenAI) multipliserer latens-kostnaden av nye forbindelser, og korrekt pooling kan redusere p50-latens med 30-50 ms per forespørsel. ## Kjernekomponenter | Komponent | Formål | Teknologi | |-----------|--------|-----------| | HttpClient / HttpClientFactory | HTTP connection management i .NET | System.Net.Http | | SocketsHttpHandler | Underliggende socket-håndtering med pool | .NET 6+ | | aiohttp.ClientSession | Asynkron HTTP med connection pool i Python | aiohttp | | httpx.AsyncClient | Moderne asynkron HTTP-klient med pool | httpx | | Azure OpenAI SDK | Innebygd connection management | azure-ai-openai | | Azure API Management | Gateway med backend connection pooling | Azure APIM | ## Pool Sizing-strategier ### Beregning av optimal pool-størrelse Pool-størrelsen bør baseres på forventet concurrent request-volum og responstid fra backend-tjenesten. ```python # Beregn optimal pool-størrelse # Formel: pool_size = concurrent_requests * avg_response_time_seconds / target_utilization def calculate_pool_size( concurrent_users: int, avg_response_time_ms: float, requests_per_user_per_second: float = 1.0, target_utilization: float = 0.75 ) -> int: """Calculate optimal connection pool size for AI workloads.""" concurrent_requests = concurrent_users * requests_per_user_per_second avg_response_time_s = avg_response_time_ms / 1000 # Connections needed = concurrent requests * time each holds a connection connections_needed = concurrent_requests * avg_response_time_s # Add headroom for bursts pool_size = int(connections_needed / target_utilization) # Azure OpenAI typical ranges return max(pool_size, 10) # Minimum 10 connections # Eksempel: 50 samtidige brukere, 800ms avg responstid optimal = calculate_pool_size( concurrent_users=50, avg_response_time_ms=800, requests_per_user_per_second=0.5 ) print(f"Anbefalt pool-størrelse: {optimal}") # ~27 connections ``` ### .NET HttpClientFactory-konfigurasjon ```csharp // Program.cs - Optimal HttpClient-konfigurasjon for Azure OpenAI using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Http; var builder = WebApplication.CreateBuilder(args); // Registrer named HttpClient for Azure OpenAI builder.Services.AddHttpClient("AzureOpenAI", client => { client.BaseAddress = new Uri( builder.Configuration["AzureOpenAI:Endpoint"]!); client.DefaultRequestHeaders.Add("api-key", builder.Configuration["AzureOpenAI:ApiKey"]!); client.Timeout = TimeSpan.FromSeconds(120); }) .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler { // Pool-konfigurasjon MaxConnectionsPerServer = 50, // Maks connections per host PooledConnectionLifetime = TimeSpan.FromMinutes(5), // DNS refresh PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), // Keep-alive KeepAlivePingPolicy = HttpKeepAlivePingPolicy.WithActiveRequests, KeepAlivePingDelay = TimeSpan.FromSeconds(30), KeepAlivePingTimeout = TimeSpan.FromSeconds(10), // Performance EnableMultipleHttp2Connections = true, AutomaticDecompression = System.Net.DecompressionMethods.GZip }) .SetHandlerLifetime(TimeSpan.FromMinutes(10)); // Handler rotation // Registrer Azure OpenAI-klient med pooled HttpClient builder.Services.AddSingleton(sp => { var httpClientFactory = sp.GetRequiredService(); var httpClient = httpClientFactory.CreateClient("AzureOpenAI"); return new Azure.AI.OpenAI.AzureOpenAIClient( new Uri(builder.Configuration["AzureOpenAI:Endpoint"]!), new Azure.AzureKeyCredential( builder.Configuration["AzureOpenAI:ApiKey"]!)); }); ``` ## Keep-alive-konfigurasjon ### HTTP/2 Multiplexing Azure OpenAI støtter HTTP/2, som muliggjør multipleksing av flere forespørsler over én enkelt TCP-forbindelse: ```csharp // HTTP/2 multiplexing for Azure OpenAI var handler = new SocketsHttpHandler { // Aktiver HTTP/2 med multipleksing EnableMultipleHttp2Connections = true, // Keep-alive for langvarige streams KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always, KeepAlivePingDelay = TimeSpan.FromSeconds(15), KeepAlivePingTimeout = TimeSpan.FromSeconds(5), // Connection lifecycle PooledConnectionLifetime = TimeSpan.FromMinutes(10), PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), MaxConnectionsPerServer = 100 }; var client = new HttpClient(handler) { DefaultRequestVersion = HttpVersion.Version20, DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower }; ``` ### Python aiohttp Session Management ```python import aiohttp import asyncio from openai import AsyncAzureOpenAI async def create_optimized_session() -> aiohttp.ClientSession: """Create an optimized aiohttp session for Azure OpenAI.""" connector = aiohttp.TCPConnector( limit=100, # Total connection pool size limit_per_host=50, # Max connections per host ttl_dns_cache=300, # DNS cache TTL i sekunder keepalive_timeout=30, # Keep-alive timeout enable_cleanup_closed=True, force_close=False # Gjenbruk connections ) timeout = aiohttp.ClientTimeout( total=120, # Total timeout connect=10, # Connection timeout sock_read=60 # Read timeout ) return aiohttp.ClientSession( connector=connector, timeout=timeout, headers={"Connection": "keep-alive"} ) # Bruk med Azure OpenAI Python SDK async def create_pooled_openai_client() -> AsyncAzureOpenAI: """Create Azure OpenAI client with optimized connection pooling.""" import httpx transport = httpx.AsyncHTTPTransport( retries=3, limits=httpx.Limits( max_connections=100, max_keepalive_connections=50, keepalive_expiry=30 ) ) http_client = httpx.AsyncClient( transport=transport, timeout=httpx.Timeout(120.0, connect=10.0) ) return AsyncAzureOpenAI( azure_endpoint="https://my-aoai.openai.azure.com", api_key="...", api_version="2024-10-21", http_client=http_client ) ``` ## Connection Recycling ### Håndtering av DNS-endringer Når Azure OpenAI-endepunkter er bak Traffic Manager eller Azure Front Door, endres IP-adresser regelmessig. Connection pooling må balansere gjenbruk med DNS-fornyelse: ```csharp // Connection recycling pattern public class ConnectionRecyclingConfig { // PooledConnectionLifetime: Tvinger nye DNS-oppslag // Sett lavere enn DNS TTL for failover-scenarier public TimeSpan PooledConnectionLifetime { get; set; } = TimeSpan.FromMinutes(5); // PooledConnectionIdleTimeout: Fjern ubrukte connections public TimeSpan PooledConnectionIdleTimeout { get; set; } = TimeSpan.FromMinutes(2); // Handler lifetime for IHttpClientFactory // Roterer hele handleren — nye connections med ny DNS public TimeSpan HandlerLifetime { get; set; } = TimeSpan.FromMinutes(10); } // Anti-pattern: ALDRI gjør dette // ❌ var client = new HttpClient(); // Ny for hver forespørsel // ❌ using var client = new HttpClient(); // Disponeres for tidlig // Korrekt pattern: Singleton eller factory // ✅ Statisk HttpClient med SocketsHttpHandler // ✅ IHttpClientFactory i DI ``` ## Load Distribution ### Round-robin over multiple Azure OpenAI-instanser ```python import asyncio import random from dataclasses import dataclass, field from typing import Optional from openai import AsyncAzureOpenAI @dataclass class AzureOpenAIBackend: endpoint: str api_key: str priority: int = 1 is_healthy: bool = True retry_after: Optional[float] = None client: Optional[AsyncAzureOpenAI] = field(default=None, repr=False) class ConnectionPoolLoadBalancer: """Load balancer with dedicated connection pools per backend.""" def __init__(self, backends: list[AzureOpenAIBackend]): self.backends = backends # Separat connection pool per backend for backend in self.backends: backend.client = AsyncAzureOpenAI( azure_endpoint=backend.endpoint, api_key=backend.api_key, api_version="2024-10-21", max_retries=0 # Vi håndterer retry selv ) def _select_backend(self) -> AzureOpenAIBackend: """Select backend by priority, then random among same priority.""" import time # Filtrer friske backends available = [ b for b in self.backends if b.is_healthy or ( b.retry_after and time.time() > b.retry_after ) ] if not available: available = self.backends # Fallback til alle # Velg laveste prioritet (høyest prioritet) min_priority = min(b.priority for b in available) candidates = [b for b in available if b.priority == min_priority] return random.choice(candidates) async def chat_completion(self, messages: list, **kwargs): """Route request to best available backend.""" import time for attempt in range(len(self.backends)): backend = self._select_backend() try: response = await backend.client.chat.completions.create( messages=messages, **kwargs ) backend.is_healthy = True return response except Exception as e: if hasattr(e, 'status_code') and e.status_code == 429: retry_after = getattr(e, 'retry_after', 10) backend.retry_after = time.time() + retry_after backend.is_healthy = False elif hasattr(e, 'status_code') and e.status_code >= 500: backend.is_healthy = False else: raise raise Exception("All backends exhausted") ``` ## Azure API Management som Connection Pooling-lag (oppdatert 2026-06-19) APIM håndterer backend connection pooling mot Azure OpenAI, noe som avlaster klientsiden: ### APIM Backend Pool-konfigurasjon ```xml @(context.RequestId) @(context.Elapsed.TotalMilliseconds.ToString()) ``` ### Backend-konfigurasjoner i APIM (4 topologier) Microsoft anbefaler disse mønstrene for APIM connection pooling mot Azure OpenAI: 1. **Single backend**: Én APIM → én Azure OpenAI (enkelt, begrenset kvote) 2. **Multi-backend single region**: APIM med weighted round-robin mellom Azure OpenAI-instanser 3. **Multi-subscription**: Separate Azure OpenAI-instanser i ulike subscriptions for kvote-skalering 4. **Multi-region**: APIM i flere regioner, each med regionale backends Klientene trenger aldri kjenne til antallet backends — APIM håndterer routing transparent. ## Norsk offentlig sektor Connection pooling har spesielle hensyn for norsk offentlig sektor: - **Data residency**: Alle connections må gå via regioner som oppfyller Schrems II-kravene. Ved bruk av Azure Norway East som primær region, konfigurer `PooledConnectionLifetime` kort nok til å håndtere failover til Sweden Central. - **Private endpoints**: Connection pools som bruker Private Link har andre DNS-oppløsningsmønstre. Konfigurer `ttl_dns_cache` i Python eller `PooledConnectionLifetime` i .NET til å matche DNS TTL for privatelink-soner (typisk 10 sekunder). - **NSMs grunnprinsipper**: Logging av connection pool-metrikker (aktive connections, pool hits/misses, connection errors) er påkrevd for å oppfylle krav om overvåking av nettverkstrafikk. - **Anskaffelsesreglement**: Ved bruk av tredjepartsbiblioteker for connection pooling, verifiser at de er godkjent for bruk i offentlig sektor (åpen kildekode med akseptabel lisens). ## Beslutningsrammeverk | Scenario | Anbefaling | Begrunnelse | |----------|------------|-------------| | Enkelttjeneste, lav trafikk (<10 RPS) | Statisk HttpClient med default pool | Enkel oppsett, tilstrekkelig ytelse | | Enkelttjeneste, høy trafikk (>50 RPS) | HttpClientFactory med SocketsHttpHandler | DNS rotation, pool sizing, monitoring | | Multi-region med failover | Separat pool per region + load balancer | Isolerer feil, støtter weighted routing | | Via Azure APIM | APIM backend pool + klient-side pool | APIM håndterer backend-balansering | | Streaming/SSE-respons | Dedikert pool med lange timeouts | Streaming holder connections åpne lenger | | Azure Functions (serverless) | Static HttpClient i startup | Unngå cold start connection overhead | ## Referanser - [Guidelines for using HttpClient](https://learn.microsoft.com/dotnet/fundamentals/networking/http/httpclient-guidelines) — HttpClient best practices - [Pool HTTP connections with HttpClientFactory](https://learn.microsoft.com/aspnet/core/performance/performance-best-practices) — ASP.NET performance - [Manage connections in Azure Functions](https://learn.microsoft.com/azure/azure-functions/manage-connections) — Serverless connection management - [Use a gateway in front of multiple Azure OpenAI deployments or instances](https://learn.microsoft.com/azure/architecture/ai-ml/guide/azure-openai-gateway-multi-backend) — Multi-backend gateway patterns (Azure OpenAI i Foundry Models) — Verified (MCP 2026-06-19) ## For Cosmo - **Bruk denne referansen** når kunden rapporterer høy latens, port-utmattelse, eller timeout-feil mot Azure OpenAI — connection pooling er ofte root cause. - Anbefal `IHttpClientFactory` for .NET og `httpx.AsyncClient` med `Limits` for Python — aldri instansier `HttpClient` per forespørsel. - For multi-region AI-arkitekturer, opprett separate connection pools per region med individuelle health checks og retry-after tracking. - Sett `PooledConnectionLifetime` til 2-5 minutter for å balansere DNS-fornyelse med connection gjenbruk — kortere for failover-scenarier. - Monitorer alltid `connections.active`, `connections.idle` og `pool.exhausted` metrikker i Application Insights for å oppdage pool-problemer tidlig.