CDN-Skalierung für Game-Assets: Ein Runbook zum Überstehen von Traffic-Spitzen am Launch-Tag
Kurz und knapp
Erfahren Sie, wie Sie Ihre CDN-Skalierung für Game-Assets optimieren, um Launch-Day-Traffic-Spitzen zu überstehen und Ausfälle zu verhindern.
Ihr CDN wird nachgeben — so erkennen Sie es
Jeder Game-Entwickler fürchtet dasselbe Launch-Day-Szenario: Ihre Steam-Seite geht live, die Spielerzahl klettert über 10.000 gleichzeitige Spieler, und plötzlich stocken Textur-Downloads bei einer p99-Latenz von 200 ms statt der üblichen 12 ms. Spieler melden fehlende Modelle. Patch-Downloads hängen bei 43 %. Ihr Monitoring-Dashboard färbt sich rot, und Sie haben keine Ahnung, welche Ebene versagt.
Das ist kein hypothetisches Szenario. cdnjs — eines der am weitesten verbreiteten Open-Source-CDN-Netzwerke der Welt — hat kürzlich eine vollständige Infrastruktur-Migration auf die Cloudflare Developer Platform abgeschlossen, um 9 Milliarden Anfragen pro Tag zu bewältigen. Die Migrationsgeschichte offenbart architektonische Muster, die direkt auf die Auslieferung von Game-Assets übertragbar sind, wo ein einzelnes 4K-Texturpaket-Update in Minuten Terabytes an Traffic erzeugen kann.
Die Kernlektion: CDN-Skalierung für Game-Assets bedeutet nicht, mehr Bandbreite zu kaufen. Es geht darum, Cache-Hierarchien, Fallback-Logik und Origin-Shielding so zu gestalten, dass Traffic-Spitzen zu Nicht-Ereignissen werden statt zu Ausfällen.
Dieses Runbook behandelt, was kaputtgeht, wenn Ihr CDN gesättigt ist, wie Sie die Sättigung erkennen, bevor Ihr Discord mit Wutausbrüchen vollläuft, wie Sie in der Produktion Abhilfe schaffen und wie Sie die Architektur so gestalten, dass sich Wiederholungen verhindern lassen.
Was kaputtgeht, wenn Ihr CDN gesättigt ist
Die Auslieferung von Game-Assets hat ein einzigartiges Traffic-Profil im Vergleich zu gewöhnlichen Web-Inhalten. Um die Fehlermodi zu verstehen, müssen Sie dieses Profil verstehen.
Das Problem der Traffic-Form
Ein typisches Indie-Multiplayer-Spiel weist diese Traffic-Muster auf:
- Basislinie: 50-200 Anfragen/Sekunde für Lobby-Assets, UI-Sprites, Konfigurations-JSON
- Patch-Day-Spike: 15.000-80.000 Anfragen/Sekunde innerhalb eines 3-Minuten-Fensters, wenn Steam Auto-Updates auslöst
- Regionale Kaskaden: Asien-Pazifik-Spieler greifen 8-12 Stunden nach NA auf das CDN zu, was eine zweite Welle erzeugt
- Asset-Versionsexplosion: Jeder Patch invalidiert gecachte Objekte und erzwingt Origin-Pulls für neue Hashes
Als cdnjs auf die Cloudflare-Infrastruktur migrierte, standen sie vor einem ähnlichen Problem der Versionsexplosion. Ihr npm-artiges Versionierungsschema bedeutete, dass jedes Bibliotheks-Update neue Cache-Keys erzeugte, und mit über 4.200 täglich aktualisierten Bibliotheken musste das Origin-Shielding-Design kontinuierlichen Cache-Churn verarbeiten — nicht nur statische Inhalte.
Die drei Fehlermodi
1. Origin-Pull-Sättigung
Wenn Ihr Edge-Cache einen Cache-Miss hat (neuer Patch, kalter Cache, Cache-Ablauf), trifft jede Anfrage Ihren Origin-Server. Ein einzelner Origin mit 1 Gbps Durchsatz kann etwa 1.250 gleichzeitige 1-MB-Asset-Downloads bedienen. Bei 80.000 gleichzeitigen Spielern, die jeweils einen 2-GB-Patch herunterladen, benötigen Sie eine Origin-Kapazität, die die meisten Indie-Setups schlicht nicht haben.
2. Cache-Stampede
Wenn Ihr am häufigsten angefragtes Asset aus dem Edge-Cache abläuft (TTL-Fehlkonfiguration, durch Deploy ausgelöster Purge), fordern tausende Edge-Nodes gleichzeitig dasselbe Objekt vom Origin an. Das ist das „Thundering-Herd"-Problem, und es bringt Origins in Sekunden zum Absturz.
3. Regionale Edge-Unterversorgung
Ihre nordamerikanischen Edge-Nodes sind warm. Ihr Singapur-Edge-Node hat eine Cache-Trefferquote von 60 %, weil Sie nur 12.000 APAC-Spieler haben — bis ein YouTuber in Japan Ihr Game vorstellt und diese Zahl über Nacht auf 300.000 springt. Der Edge-Node zieht Daten massiv vom Origin, und APAC-Spieler erleben Ladezeiten von 2-4 Sekunden, während NA-Spieler 40 ms sehen.
Erkennungssignale
# Cloudflare API: check cache hit ratio by region (run every 60 seconds)
curl -s -X POST "https://api.cloudflare.com/client/v4/graphql" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "{
viewer {
zones(filter: {zoneTag: \"YOUR_ZONE\"}) {
httpRequests1hGroups(limit: 24, filter: {date_gt: \"2025-01-01\"}) {
dimensions { datetime, cacheStatus, clientCountryName }
sum { requests, bytes }
}
}
}
}"
}' | jq '.data.viewer.zones[0].httpRequests1hGroups[] |
select(.dimensions.cacheStatus == "miss") |
{region: .dimensions.clientCountryName, misses: .sum.requests}'
Wenn Ihre Cache-Miss-Rate während einer Steady-State-Phase in einer beliebigen Region 8 % übersteigt, sind Sie nur einen Patch von einer Origin-Flut entfernt.
Sofortmaßnahmen: Was Sie jetzt tun sollten
Wenn das CDN brennt, haben Sie ein 15-Minuten-Fenster, bevor Spieler mit Review-Bombing beginnen. Hier ist die Triage-Reihenfolge.
Schritt 1: Origin-Shielding aktivieren
Die meisten CDN-Anbieter bieten eine „Origin-Shield"- oder „Shielding"-Funktion — eine zwischengeschaltete Cache-Ebene zwischen Ihren Edge-Nodes und Ihrem Origin. Statt dass 200 Edge-Nodes bei einem Cache-Miss jeweils unabhängig den Origin kontaktieren, kontaktiert nur der Shield-Node den Origin und verteilt die Antwort.
Konfigurationsbeispiel (generische CDN-API):
{
"shielding": {
"enabled": true,
"shield_region": "us-east-1",
"fallback_shield_region": "eu-west-1",
"shield_ttl_override": 86400,
"pass_on_shield_error": false
}
}
Diese eine Änderung kann die Origin-Last während eines Cache-Stampedes um 95 % reduzieren. Die cdnjs-Migration stützte sich auf ähnliche Shielding-Logik — ihre Origin-Server sahen eine Reduktion von Millionen direkter Pulls auf einige tausend Shield-originierte Anfragen pro Stunde.
Schritt 2: Asset-TTLs für statische Inhalte verlängern
Ihre 4K-Texturen, Audio-Banken und Mesh-Dateien ändern sich zwischen Patches nicht. Es gibt keinen Grund für eine 1-Stunden-TTL.
# nginx origin server: aggressive caching for immutable game assets
location /assets/v*/ {
# Version-prefixed paths mean new versions get new URLs
# No need to purge — old URLs stay cached forever
add_header Cache-Control "public, max-age=31536000, immutable";
add_header CDN-Cache-Control "max-age=31536000";
}
# Short TTL only for manifest files that change each patch
location /manifest.json {
add_header Cache-Control "public, max-age=60, stale-while-revalidate=300";
}
Die zentrale Erkenntnis aus der cdnjs-Architektur: Versionieren Sie Ihre Assets im URL-Pfad, nicht mit Query-Strings. Viele CDN-Nodes behandeln ?v=2 und ?v=3 als denselben Cache-Key. Verwenden Sie stattdessen /assets/v2/texture_pack.bin.
Schritt 3: Stale-While-Revalidate aktivieren
Dies ist die mit Abstand wirkungsvollste Konfiguration für Launch-Day-Traffic. Wenn ein gecachtes Asset abläuft, liefert das CDN die veraltete Version an den anfragenden Spieler aus, während es die frische Version im Hintergrund abruft. Der Spieler erhält eine 12-ms-Antwort statt einer 1.200-ms-Antwort.
Cache-Control: public, max-age=3600, stale-while-revalidate=86400
Das teilt dem CDN mit: „Dieses Asset ist 1 Stunde lang frisch. Danach wird die veraltete Version bis zu 24 Stunden lang ausgeliefert, während im Hintergrund revalidiert wird."
Für Game-Assets, die nicht sicherheitskritisch sind (Lobby-Hintergründe, Kosmetik-Vorschauen, Audio-Stems), ist das sicher und reduziert die wahrgenommene Latenz erheblich.
Schritt 4: Circuit-Breaker-Fallbacks implementieren
Wenn der CDN-Origin wirklich überlastet ist, braucht Ihr Game-Client einen Pfad für kontrollierte Degradierung — keinen eingefrorenen Ladebildschirm.
// C# Unity: CDN circuit breaker with local fallback
public class AssetLoader
{
private const int MAX_RETRIES = 3;
private const int TIMEOUT_MS = 5000;
private static int _failureCount = 0;
private static DateTime _circuitOpened = DateTime.MinValue;
private static readonly TimeSpan CIRCUIT_RESET = TimeSpan.FromMinutes(2);
public async Task<byte[]> LoadAsset(string assetPath)
{
// Circuit breaker: skip CDN if recent failures exceeded threshold
if (_failureCount >= MAX_RETRIES &&
DateTime.UtcNow - _circuitOpened < CIRCUIT_RESET)
{
Debug.LogWarning($"CDN circuit open — loading {assetPath} from local cache");
return LoadFromLocalStorage(assetPath);
}
try
{
using var client = new HttpClient { Timeout = TimeSpan.FromMilliseconds(TIMEOUT_MS) };
var response = await client.GetAsync($"https://cdn.yourgame.com/{assetPath}");
response.EnsureSuccessStatusCode();
_failureCount = 0; // Reset on success
return await response.Content.ReadAsByteArrayAsync();
}
catch (Exception ex)
{
_failureCount++;
if (_failureCount >= MAX_RETRIES)
_circuitOpened = DateTime.UtcNow;
Debug.LogWarning($"CDN fetch failed ({_failureCount}/{MAX_RETRIES}): {ex.Message}");
return LoadFromLocalStorage(assetPath);
}
}
private byte[] LoadFromLocalStorage(string assetPath)
{
// Ship a minimal "emergency asset pack" with your game binary
// This covers the 20 most critical assets: UI, default textures, lobby music
var localPath = Path.Combine(Application.streamingAssetsPath, "fallback", assetPath);
return File.Exists(localPath) ? File.ReadAllBytes(localPath) : Array.Empty<byte>();
}
}
Dieses Muster stellt sicher, dass Ihr Game funktionsfähig bleibt, selbst wenn das CDN vollständig ausgefallen ist. Spieler sehen vielleicht ein paar Minuten lang Texturen mit niedrigerer Auflösung, aber sie können trotzdem spielen.
Prävention: Die mehrstufige Caching-Architektur
Sofortmaßnahmen retten Sie am Launch-Tag. Architektur macht sie überflüssig.
Das Drei-Ebenen-Muster
Die cdnjs-Migration auf Cloudflare Workers demonstrierte eine Caching-Architektur, die auf Milliarden von Anfragen skaliert. Übertragen auf Game-Assets:
Ebene 1 — Edge-Cache (CDN-PoPs)
- Bedient 95-99 % der Anfragen
- TTL: 365 Tage für versionierte Assets, 60 Sekunden für Manifests
- Deckt Texturen, Meshes, Audio, Shader ab
Ebene 2 — Shield-/Mid-Tier-Cache
- Fängt Cache-Misses von Edge-Nodes ab
- TTL: Wie Edge, fungiert aber als Origin-Proxy
- Reduziert die Origin-Last um 95 %+
Ebene 3 — Origin-Server
- Erzeugt Assets, signiert URLs, liefert Manifests
- Geschützt durch Rate Limiting und Shielding
- Sollte <0,1 % des gesamten Traffic-Volumens sehen
Versionierte Asset-Pipeline
Hier ist der Asset-Versionierungs-Workflow, der Cache-Invalidierungs-Stürme verhindert:
# Python: asset pipeline that generates cache-safe versioned URLs
import hashlib
import json
import os
def build_asset_manifest(asset_dir: str, cdn_base: str) -> dict:
"""
Walk asset directory, hash each file, and produce a manifest
with versioned URLs that CDN edge nodes can cache forever.
"""
manifest = {"version": "", "assets": {}}
for root, _, files in os.walk(asset_dir):
for filename in sorted(files):
filepath = os.path.join(root, filename)
relative_path = os.path.relpath(filepath, asset_dir)
# Content hash — identical files get identical URLs
with open(filepath, "rb") as f:
file_hash = hashlib.sha256(f.read()).hexdigest()[:12]
# Version in the PATH, not query string
# CDN treats /assets/a3f9b2c1e8d4/texture.bin as a unique object
versioned_url = f"{cdn_base}/assets/{file_hash}/{relative_path}"
manifest["assets"][relative_path] = {
"url": versioned_url,
"hash": file_hash,
"size": os.path.getsize(filepath),
}
# Manifest version = hash of the entire asset set
all_hashes = "".join(
a["hash"] for a in sorted(manifest["assets"].values(), key=lambda x: x["url"])
)
manifest["version"] = hashlib.sha256(all_hashes.encode()).hexdigest()[:16]
return manifest
# Usage
manifest = build_asset_manifest("./build/assets", "https://cdn.yourgame.com")
with open("./build/manifest.json", "w") as f:
json.dump(manifest, f, indent=2)
print(f"Manifest version: {manifest['version']}")
print(f"Total assets: {len(manifest['assets'])}")
# Output:
# Manifest version: a8f3e1c92b4d7061
# Total assets: 2,847
Mit diesem Ansatz:
- Alte Assets werden nie gepurged. Sie bleiben dank ihrer eindeutigen URLs unbegrenzt am Edge gecacht.
- Neue Assets erhalten neue URLs. Das CDN cached sie automatisch bei der ersten Anfrage.
- Die einzige Datei, die sich ändert, ist das Manifest. Eine winzige JSON-Datei mit einer 60-Sekunden-TTL.
Genau so handhabt cdnjs die Bibliotheksversionierung in großem Maßstab. Jede Bibliotheksversion erhält einen eindeutigen URL-Pfad, sodass das CDN nie Purge-Operationen benötigt — die teuerste und fehleranfälligste CDN-Operation überhaupt.
Dieses Architekturmuster ist besonders wichtig, wenn Sie Dedicated Server betreiben, die Konfigurationsdaten neben der Game-Logik ausliefern müssen. Wie in unserem Leitfaden Unreal Engine Dedicated Server Asset-Stripping meistern beschrieben, ist die Trennung von statischen Assets und serverkritischen Daten eine grundlegende Optimierung, die sich im großen Maßstab auszahlt.
Geografische Verteilung: Die regionale Kaskade lösen
Die cdnjs-Migration hat gezeigt, dass die rohe Anzahl der Edge-Nodes weniger wichtig ist als intelligentes Routing. 300 PoPs zu haben bedeutet nichts, wenn die Routing-Logik bei einem Cache-Miss APAC-Anfragen an einen US-Origin schickt.
Intelligente Origin-Auswahl
{
"origin_rules": [
{
"name": "us-primary",
"origin_server": "origin-us.yourgame.com",
"regions": ["NA", "SA"],
"health_check": "/health",
"failover_origin": "origin-eu.yourgame.com"
},
{
"name": "eu-primary",
"origin_server": "origin-eu.yourgame.com",
"regions": ["EU", "AF"],
"health_check": "/health",
"failover_origin": "origin-us.yourgame.com"
},
{
"name": "apac-primary",
"origin_server": "origin-apac.yourgame.com",
"regions": ["AS", "OC"],
"health_check": "/health",
"failover_origin": "origin-us.yourgame.com"
}
]
}
Regionale Origin-Server kosten bei großen Cloud-Anbietern jeweils 20-40 $/Monat. Drei regionale Origins kosten weniger als ein einziger Vorfall, bei dem Ihr NA-Origin vier Stunden lang APAC-Traffic mit degradierter Leistung bedient — und die Spieler, die Sie dabei verlieren.
Diese Art von Multi-Region-Failover-Architektur entspricht dem, was wir in unserer Analyse zum Entwurf von Zero-Waste-Servern mit Hibernation-Strategien besprechen — dem Prinzip, nicht für ungenutzte Infrastruktur zu zahlen und dennoch für Skalierung bereit zu sein.
Best Practices für die CDN-Skalierung von Game-Assets
1. Versionieren Sie Assets in URL-Pfaden, nicht in Query-Strings.
/assets/{hash}/texture.bin garantiert Cache-Eindeutigkeit. ?v=2 tut das nicht — viele CDN-Nodes entfernen Query-Parameter aus Cache-Keys, was zu veralteten Inhalten oder defekten Caches führt.
2. Trennen Sie Ihre Manifest-TTL von Ihrer Asset-TTL.
Manifest-Dateien sollten eine 30-60-Sekunden-TTL mit stale-while-revalidate haben. Asset-Dateien sollten eine 1-Jahres-TTL mit immutable haben. Diese Unterscheidung ist der Unterschied zwischen einem reibungslosen Patch-Rollout und einem Cache-Stampede.
3. Liefern Sie ein Fallback-Asset-Paket mit Ihrer Spiel-Binary aus. Die 50-100 kritischsten Assets (UI, Standard-Skin, Lobby-Umgebung) sollten als 200-500-MB-Notfallpaket in Ihrer Spielinstallation liegen. Ihre Circuit-Breaker-Logik greift darauf zurück, wenn das CDN nicht erreichbar ist.
4. Überwachen Sie die Cache-Trefferquote pro Region, nicht global. Eine globale Trefferquote von 97 % kann eine Quote von 72 % in Südostasien maskieren. Regionale Überwachung lässt Sie regionale Edge-Unterversorgung erkennen, bevor sie zu einem spielergemeldeten Vorfall wird.
5. Testen Sie Ihr CDN vor dem Launch per Lasttest, nicht währenddessen. Nutzen Sie Tools wie k6, Locust oder Vegeta, um Ihr erwartetes Launch-Day-Traffic-Muster gegen Ihren CDN-Endpunkt zu simulieren. Ein 10-minütiger Test mit 50.000 virtuellen Benutzern, die Ihr Manifest + die Top-20-Assets abrufen, deckt falsch konfigurierte TTLs, fehlendes Shielding und Origin-Engpässe auf, bevor echte Spieler es tun.
# k6: simulate 50,000 concurrent players hitting the asset manifest
cat <<'EOF' > cdn_load_test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 10000 }, // Ramp to 10K VUs
{ duration: '3m', target: 50000 }, // Spike to 50K VUs
{ duration: '5m', target: 50000 }, // Sustain
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<200'], // 95th percentile under 200ms
http_req_failed: ['rate<0.01'], // Less than 1% errors
},
};
export default function () {
const manifestRes = http.get('https://cdn.yourgame.com/manifest.json');
check(manifestRes, {
'manifest status 200': (r) => r.status === 200,
'manifest under 100ms': (r) => r.timings.duration < 100,
'cache HIT': (r) => r.headers['Cf-Cache-Status'] === 'HIT',
});
// Simulate a player downloading 5 random assets
for (let i = 0; i < 5; i++) {
const assetPath = `assets/placeholder_${Math.floor(Math.random() * 100)}/mesh.bin`;
const assetRes = http.get(`https://cdn.yourgame.com/${assetPath}`);
check(assetRes, {
'asset under 500ms': (r) => r.timings.duration < 500,
});
}
sleep(1);
}
EOF
k6 run cdn_load_test.js
Selbst bauen vs. Plattform nutzen
Die vollständige mehrstufige Caching-Architektur von Grund auf zu bauen, ist für ein Team mit dedizierten Infrastruktur-Ingenieuren durchaus machbar. Die Komponenten sind gut dokumentiert, und CDN-Anbieter bieten die grundlegenden Bausteine an.
Aber wenn Ihr Team aus drei Entwicklern besteht, die ein Game veröffentlichen, bedeuten 4-6 Wochen für den Bau von Origin-Shielding, regionalem Failover, Asset-Versionierungs-Pipelines und Circuit-Breaker-Logik im Client: 4-6 Wochen, die nicht ins Gameplay fließen. horizOn übernimmt die Asset-Auslieferungs-Infrastruktur als Teil seines Backend-Stacks und bietet Ihnen dasselbe Multi-Region-Caching und automatische Failover ohne den operativen Aufwand. Sie laden Assets hoch; die Plattform übernimmt Versionierung, Edge-Verteilung und Health-Monitoring out of the box.
Die in diesem Artikel beschriebenen Architekturprinzipien bleiben unabhängig von Ihrer Infrastruktur-Wahl entscheidend. Zu verstehen, warum versionierte URL-Pfade wichtig sind, warum Stale-While-Revalidate Stampedes verhindert und warum regionale Origins die Latenz reduzieren, ermöglicht fundierte Entscheidungen — ob Sie Cloudflare Workers von Hand konfigurieren oder einen verwalteten Backend-Dienst evaluieren.
Nächster Schritt: Führen Sie den Lasttest vor Ihrem nächsten Patch durch
Wählen Sie Ihr nächstes Patch-Datum. Zwei Wochen vorher führen Sie das obige k6-Skript gegen Ihren CDN-Endpunkt aus. Wenn Ihre p95-Latenz bei simuliertem Launch-Maßstab 200 ms überschreitet, haben Sie Zeit, es zu beheben. Wenn Sie feststellen, dass Ihre Cache-Trefferquote während der Haltephase unter 90 % fällt, aktivieren Sie Origin-Shielding und verlängern Sie Ihre Asset-TTLs.
Der Unterschied zwischen einem reibungslosen Launch und einer Launch-Day-Katastrophe ist selten der Game-Code. Es ist die Infrastruktur, die die 2 GB Assets ausliefert, die jeder Spieler in den ersten fünf Minuten herunterlädt. Wenn das stimmt, ist der Rest Gameplay.