Özel Oyun Sunucuları: GameLift ve Agones ile Düzenleme
Her rekabetçi çok oyunculu oyun, görünmez ancak kritik bir altyapı tarafından desteklenir: özel oyun sunucusu. Oyunculardan birinin sunucu olarak hareket ettiği eşler arası mimariden farklı olarak (tüm adalet sorunlarıyla birlikte) ve bunun sonucunda hile yapılması), özel bir sunucu ve oyunun yetkili simülasyonunu çalıştıran tarafsız bir makine. oyun, tüm oyunculardan girdi alır ve güncellenmiş durumu her birine dağıtır.
Talebe göre dinamik olarak ölçeklenerek bu sunuculardan on binlercesini verimli bir şekilde yönetin ve maliyetleri en aza indirmek, modern oyun arka ucundaki en karmaşık mühendislik zorluklarından biridir. bunda Bu makalede oyun sunucusu orkestrasyonu için önde gelen iki çözümü inceleyeceğiz: AWS GameLift, Amazon'un yönetilen hizmeti, e AgonlarKubernetes üzerinde açık kaynaklı çerçeve geliştirildi Google ve Ubisoft'tan. İkisi arasında nasıl seçim yapacağımızı, nasıl yapılandıracağımızı ve sistemlere nasıl entegre edeceğimizi göreceğiz. ölçeklenebilir ve uygun maliyetli bir oyun altyapısı oluşturmak için eşleştirme.
Ne Öğreneceksiniz
- Özel bir oyun sunucusunun mimarisi ve yaşam döngüsü
- AWS GameLift: filo yönetimi, oturumlar ve FlexMatch ile entegrasyon
- Kubernetes'te Agones: GameServer CRD, Filo ve Otomatik Ölçeklendirme
- FleetIQ Adaptörü: Maliyetlerden %90'a kadar tasarruf sağlayan Spot Bulut Sunucuları
- Global Accelerator ile çok bölgeli dağıtım modeli
- Yüksek oranda kullanılabilir oyun sunucuları için izleme ve sağlık kontrolü
Özel Bir Oyun Sunucusunun Mimarisi
Düzenleme araçlarına geçmeden önce bir oyun sunucusunun tam olarak ne yaptığını anlamak önemlidir. adanmışlığı ve yaşam döngüsünü nasıl yönettiği. Tipik bir özel sunucu şu durumlardan geçer:
| Durum | Tanım | Tipik süre |
|---|---|---|
| BAŞLANGIÇ | Süreç başlar, varlıkları yükler ve orkestrasyon sistemiyle kayıtları kaydeder | 2-10 saniye |
| HAZIR | Bağlantıları kabul etmeye hazır, bir oturuma atanmayı bekliyor | değişken |
| TAHSİS EDİLDİ | Bir oyuna atanır, belirtilen oyuncuları kabul eder | maç süresi |
| REZERVE | Gelecekteki bir oturum için rezervasyon yapıldı, başkaları için uygun değil | saniye-dakika |
| KAPAT | Oyun bitti, süreç kapanır ve kaynağı serbest bırakır | 2-5 saniye |
Düzenleme sistemi her sunucunun durumunu izlemeli, oyuncuları mevcut sunuculara dağıtmalı, arızaları yönetin ve yüke göre filoyu otomatik olarak ölçeklendirin. Buradaki zorluk, bu operasyonların bunların saniyeler içinde gerçekleşmesi gerekiyor: hiçbir oyuncu bir eşleşme bulmak için 30 saniye beklemek istemez.
AWS GameLift: Yönetilen Hizmet
AWS GameLift, sunucu düzenlemenin karmaşıklığını ortadan kaldıran, tam olarak yönetilen bir hizmet sunar. Mimarisi üç ana bileşene ayrılmıştır: Filo (EC2 örnek grupları sunucuları çalıştıran), Oyun Oturumu (bir sunucuda çalışan oyun örnekleri) ve ben Oyuncu Oturumu (bir oturumdaki her oyuncu için ayrılan yuvalar).
// Integrazione GameLift SDK nel game server (Node.js)
import { GameLift } from 'aws-sdk';
import * as GameLiftServerSDK from 'gamelift-server-sdk';
class GameLiftIntegration {
private sdk = GameLiftServerSDK;
async initialize(): Promise<void> {
// Inizializza la connessione con il servizio GameLift
const initResult = await this.sdk.InitSDK();
if (!initResult.Success) {
throw new Error(`GameLift SDK init failed: ${initResult.Error}`);
}
// Registra i callback per gli eventi del lifecycle
this.sdk.ProcessReady({
onStartGameSession: this.handleStartGameSession.bind(this),
onUpdateGameSession: this.handleUpdateGameSession.bind(this),
onProcessTerminate: this.handleProcessTerminate.bind(this),
onHealthCheck: () => true, // Game server e in salute
port: 7777,
logParameters: { logPaths: ['/local/game/logs/'] }
});
console.log('GameLift: Server pronto, in attesa di sessioni');
}
private async handleStartGameSession(gameSession: GameSession): Promise<void> {
console.log(`Nuova sessione: ${gameSession.GameSessionId}`);
// Inizializza la logica di gioco
await this.initializeGameLogic(gameSession);
// Notifica GameLift che la sessione e attiva
this.sdk.ActivateGameSession();
}
private async handleProcessTerminate(): Promise<void> {
// Salva lo stato, disconnetti i giocatori
await this.saveGameState();
this.sdk.ProcessEnding();
process.exit(0);
}
// Aggiunge un giocatore alla sessione
async acceptPlayer(playerSessionId: string): Promise<void> {
const result = await this.sdk.AcceptPlayerSession(playerSessionId);
if (!result.Success) {
throw new Error(`Player non accettato: ${result.Error}`);
}
}
// Rimuove un giocatore alla sessione
async removePlayer(playerSessionId: string): Promise<void> {
await this.sdk.RemovePlayerSession(playerSessionId);
}
}
İstemci, GameLift İstemci SDK'sı veya REST API aracılığıyla bir oyun oturumu talep etmelidir. İşte nasıl yeni bir oturum başlatmak veya mevcut bir oturuma katılmak için istemci tarafı mantığını yapılandırın:
// Client: creazione sessione di gioco (TypeScript)
import AWS from 'aws-sdk';
const gamelift = new AWS.GameLift({ region: 'eu-west-1' });
interface MatchResult {
serverIp: string;
serverPort: number;
playerSessionId: string;
}
async function joinGame(playerId: string, fleetId: string): Promise<MatchResult> {
// Cerca sessioni con slot disponibili
const searchResult = await gamelift.searchGameSessions({
FleetId: fleetId,
FilterExpression: 'hasAvailablePlayerSessions=true',
SortExpression: 'creationTimeMillis ASC',
Limit: 1
}).promise();
let gameSessionId: string;
if (searchResult.GameSessions?.length) {
// Unisciti a una sessione esistente
gameSessionId = searchResult.GameSessions[0].GameSessionId!;
} else {
// Crea una nuova sessione
const newSession = await gamelift.createGameSession({
FleetId: fleetId,
MaximumPlayerSessionCount: 10,
Name: `session-${Date.now()}`,
GameProperties: [
{ Key: 'map', Value: 'arena_01' },
{ Key: 'mode', Value: 'deathmatch' }
]
}).promise();
gameSessionId = newSession.GameSession!.GameSessionId!;
}
// Crea una player session per questo giocatore
const playerSession = await gamelift.createPlayerSession({
GameSessionId: gameSessionId,
PlayerId: playerId
}).promise();
return {
serverIp: playerSession.PlayerSession!.IpAddress!,
serverPort: playerSession.PlayerSession!.Port!,
playerSessionId: playerSession.PlayerSession!.PlayerSessionId!
};
}
GameLift FlexMatch: Entegre Eşleştirme
GameLift şunları içerir: FlexMatchkuralları tanımlamanıza olanak tanıyan esnek bir eşleştirme motoru maçların kompozisyonu için karmaşık. Otomatik doldurmayı destekler (maç sırasında boş pozisyonları doldurur), ekip dengeleme, coğrafi filtreler ve beceri gereksinimleri.
// FlexMatch rule set - definisce le regole di matchmaking
const flexMatchRuleSet = {
"name": "competitive-5v5",
"ruleLanguageVersion": "1.0",
"playerAttributes": [
{
"name": "skill",
"type": "number",
"default": 1000
},
{
"name": "latency",
"type": "latencyMilliseconds"
}
],
"teams": [
{
"name": "team1",
"minPlayers": 5,
"maxPlayers": 5
},
{
"name": "team2",
"minPlayers": 5,
"maxPlayers": 5
}
],
"rules": [
{
"name": "FairTeamSkill",
"description": "Differenza skill media tra team < 150",
"type": "distance",
"measurements": [
"teams[team1].players.attributes[skill]",
"teams[team2].players.attributes[skill]"
],
"referenceValue": 150,
"maxDistance": 150
},
{
"name": "FastConnection",
"description": "Latenza max 100ms",
"type": "latency",
"maxLatency": 100
}
],
"expansions": [
{
"target": "rules[FairTeamSkill].maxDistance",
"steps": [
{ "waitTimeSeconds": 30, "value": 250 },
{ "waitTimeSeconds": 60, "value": 500 }
]
}
]
};
Agones: Kubernetes'te Oyun Sunucusu
Agonlar ve başlangıçta Google ve Ubisoft tarafından geliştirilen ve şu anda sürdürülen açık kaynaklı bir çerçeve
Kubernetes'i oyunlar için özel olarak tasarlanmış Özel Kaynak Tanımları (CRD) ile genişleten topluluktan
sunucu. Agones üç yeni Kubernetes nesnesini tanıtıyor: GameServer, Fleet e
GameServerAllocation.
Agones'un GameLift'e göre en büyük avantajı esnekliktir: onu herhangi bir kümeye dağıtabilirsiniz Kubernetes (GKE, EKS, AKS, şirket içi) ve altyapı üzerinde tam kontrol sizde. Dezavantajı şu ki GameLift'in otomatik olarak ele aldığı pek çok hususu kendiniz halletmeniz gerekir.
# Agones GameServer - manifest Kubernetes
apiVersion: agones.dev/v1
kind: GameServer
metadata:
name: my-game-server
labels:
game: "shooter"
region: "eu-west"
spec:
ports:
- name: default
portPolicy: Dynamic # Kubernetes assegna la porta dinamicamente
containerPort: 7777
protocol: UDP
health:
initialDelaySeconds: 30
periodSeconds: 5
failureThreshold: 3
sdkServer:
logLevel: Info
grpcPort: 9357
httpPort: 9358
template:
spec:
containers:
- name: game-server
image: gcr.io/myproject/game-server:v1.2.0
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
env:
- name: GAME_MODE
value: "deathmatch"
- name: MAX_PLAYERS
value: "16"
# Agones Fleet - gestisce un pool di GameServer
apiVersion: agones.dev/v1
kind: Fleet
metadata:
name: shooter-fleet
spec:
replicas: 10 # Mantieni sempre 10 server pronti
scheduling: Packed # Impacchetta i server sullo stesso nodo
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
template:
metadata:
labels:
game: "shooter"
spec:
ports:
- name: default
portPolicy: Dynamic
containerPort: 7777
protocol: UDP
template:
spec:
containers:
- name: game-server
image: gcr.io/myproject/game-server:v1.2.0
resources:
requests:
memory: "512Mi"
cpu: "500m"
---
# FleetAutoscaler - scala automaticamente la Fleet
apiVersion: autoscaling.agones.dev/v1
kind: FleetAutoscaler
metadata:
name: shooter-fleet-autoscaler
spec:
fleetName: shooter-fleet
policy:
type: Buffer
buffer:
bufferSize: 5 # Mantieni almeno 5 server READY
minReplicas: 5
maxReplicas: 100
sync:
type: FixedInterval
fixedInterval:
seconds: 30
Agones SDK'nın Oyun Sunucusuna entegrasyonu
Agones, Go, C++, Rust, Node.js ve diğer diller için SDK'lar sağlar. Oyun sunucusu iletişim kurmalıdır Durumunuzu bildirmek ve yaşam döngüsü bildirimleri almak için Agones sepetini kullanın.
// Agones SDK integration - Node.js
import AgonesSDK from '@google-cloud/agones-sdk';
class AgonesGameServer {
private sdk: AgonesSDK;
private healthInterval: NodeJS.Timer | null = null;
constructor() {
this.sdk = new AgonesSDK();
}
async start(): Promise<void> {
// Connetti al sidecar Agones
await this.sdk.connect();
console.log('Connesso ad Agones');
// Ascolta le notifiche di allocazione
this.sdk.watchGameServer((gameServer) => {
console.log('Stato server aggiornato:', gameServer.status.state);
if (gameServer.status.state === 'Allocated') {
this.handleAllocation(gameServer);
}
});
// Invia health check ogni 5 secondi
this.healthInterval = setInterval(async () => {
try {
await this.sdk.health();
} catch (err) {
console.error('Health check fallito:', err);
}
}, 5000);
// Segnala che il server e pronto
await this.sdk.ready();
console.log('Server in stato READY');
}
private async handleAllocation(gameServer: any): Promise<void> {
const labels = gameServer.objectMeta.labels;
const sessionId = labels['session-id'] || 'unknown';
console.log(`Server allocato per sessione: ${sessionId}`);
// Inizializza la logica di gioco con i parametri dell'allocazione
const annotations = gameServer.objectMeta.annotations;
await this.initGame({
sessionId,
maxPlayers: parseInt(annotations['max-players'] || '10'),
mapId: annotations['map-id'] || 'default'
});
}
async shutdown(): Promise<void> {
if (this.healthInterval) {
clearInterval(this.healthInterval);
}
await this.saveGameResults();
await this.sdk.shutdown(); // Segnala ad Agones che il server sta terminando
}
// Allocazione via API
static async allocateServer(namespace: string): Promise<AllocationResult> {
const response = await fetch(`http://agones-allocator.${namespace}:8443/gameserverallocation`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
apiVersion: 'allocation.agones.dev/v1',
kind: 'GameServerAllocation',
spec: {
selectors: [
{ matchLabels: { 'agones.dev/fleet': 'shooter-fleet' } }
],
metadata: {
labels: { 'session-id': `session-${Date.now()}` },
annotations: {
'max-players': '10',
'map-id': 'arena_01'
}
}
}
})
});
return response.json();
}
}
FleetIQ Adaptörü: Spot Bulut Sunucularıyla Maliyet Tasarrufu
Oyun sunucusu barındırmanın en büyük maliyetlerinden biri bilgi işlemdir. AWS şunu geliştirdi: Agones için GameLift FleetIQ AdaptörüAgones sunucularını çalıştırmanıza izin veren EC2 Spot Bulut Sunucularıİsteğe Bağlı bulut sunucularına kıyasla %90'a kadar tasarruf sağlar.
AWS'nin bu kapasiteye ihtiyacı olduğunda Spot Bulut Sunucuları ve ani kesinti riski. FleetIQ, kesintiyi tahmin etmek için makine öğrenimi algoritmalarını kullanarak bu riski azaltır ve geri dönüş olarak her zaman İsteğe Bağlı bulut sunucularının bir arabelleğini korur.
| Yapılandırma | Maliyet/saat (c5.xlarge) | Tasarruf | Kesinti riski |
|---|---|---|---|
| Talep üzerine | 0,192$ | - | 0% |
| Spot Bulut Sunucusu | ~0,025-0,060$ | %70-87 | %5-15 |
| Spot + FleetIQ | ~0,030-0,070$ | %63-85 | <%1 (ML tahmini) |
| Graviton Noktası | ~0,020-0,040$ | %79-90 | <%1 |
Global Hızlandırıcıyla Çok Bölgeli Mimari
Küresel oyuncu tabanına sahip bir oyun için çok bölgeli bir mimari gereklidir. tüm oyuncular için düşük gecikme. AWS Global Accelerator trafiği bölgeye yönlendiriyor yaklaşarak algılanan gecikmeyi azaltır ve bağlantı kararlılığını artırır.
// Architettura multi-region - configurazione Terraform
resource "aws_globalaccelerator_accelerator" "game_accelerator" {
name = "game-global-accelerator"
ip_address_type = "IPV4"
enabled = true
}
resource "aws_globalaccelerator_listener" "game_listener" {
accelerator_arn = aws_globalaccelerator_accelerator.game_accelerator.id
client_affinity = "SOURCE_IP" # Affinita per sessioni di gioco
protocol = "UDP"
port_range {
from_port = 7000
to_port = 8000
}
}
# Endpoint group per EU-West-1
resource "aws_globalaccelerator_endpoint_group" "eu_west" {
listener_arn = aws_globalaccelerator_listener.game_listener.id
endpoint_group_region = "eu-west-1"
traffic_dial_percentage = 100
health_check_port = 8080
health_check_protocol = "HTTP"
health_check_path = "/health"
health_check_interval_seconds = 10
threshold_count = 3
endpoint_configuration {
endpoint_id = aws_lb.eu_game_lb.arn
weight = 100
}
}
# Endpoint group per AP-Southeast-1
resource "aws_globalaccelerator_endpoint_group" "ap_southeast" {
listener_arn = aws_globalaccelerator_listener.game_listener.id
endpoint_group_region = "ap-southeast-1"
traffic_dial_percentage = 100
endpoint_configuration {
endpoint_id = aws_lb.ap_game_lb.arn
weight = 100
}
}
Karşılaştırma: GameLift ve Agones
| Kriter | AWS GameLift | Kubernetes'te Agones |
|---|---|---|
| İlk kurulum | Hızlı, tümüyle yönetilen | Karmaşık, K8'in uzmanlığını gerektirir |
| Satıcıya kilitlenme | Yüksek (yalnızca AWS) | Düşük (çoklu bulut) |
| Sabit maliyetler | Oturum + örnek başına fiyatlandırma | Yalnızca hesaplanan maliyetler |
| Çöpçatanlık | Entegre FlexMatch | Ayrı Açık Eşleşme gerektirir |
| Ölçeklendirme | Otomatik, yönetilen | Manuel + FleetAutoscaler |
| Çoklu bulut | No | Evet (GKE, EKS, AKS) |
| UDP protokolü | Si | Si |
| Spot Bulut Sunucuları | FleetIQ entegre | K8s Spot + FleetIQ Adaptörü |
GameLift'i Ne Zaman Seçmelisiniz?
Zaten AWS ekosistemine yoğun bir şekilde entegre olmuşsanız ve küçük bir ekibiniz varsa GameLift en iyi seçimdir Kubernetes uzmanlığı olmadan ve çalışma süresini en aza indirmek istiyorsunuz. Ek maliyet yönetilen hizmettir ve genellikle üretimde Kubernetes'i yönetmek için gereken personelin maliyetinden daha düşüktür.
Agones Ne Zaman Seçilmeli?
Agones, Kubernetes uzmanlığına sahip ekipler ve birden fazla bulut sağlayıcıda çalıştırılması gereken oyunlar için idealdir. veya altyapınız üzerindeki kontrolü en üst düzeye çıkarmak istediğinizde. Ayrıca zaten sahipseniz doğru seçim yerleşik bir Kubernetes altyapısı var ve bunu oyun sunucuları için kullanmak istiyor.
Sağlık Kontrolü ve İzleme
Bozulmuş oyun sunucularını daha erken tespit etmek için sağlam bir sağlık kontrol sistemi gereklidir bu da oyuncuları etkiliyor. Hem GameLift hem de Agones periyodik sağlık kontrollerini destekler, ancak Özel uygulama metriklerini uygulamak önemlidir.
// Health check endpoint per game server - Express.js
import express from 'express';
import { GameMetrics } from './metrics';
const app = express();
const metrics = new GameMetrics();
app.get('/health', (req, res) => {
const health = {
status: 'ok',
uptime: process.uptime(),
memoryMB: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
activePlayers: metrics.getActivePlayers(),
tickRate: metrics.getCurrentTickRate(),
avgLatencyMs: metrics.getAverageLatency()
};
// Considera degraded se tickrate scende sotto il 80% del target
if (health.tickRate < metrics.targetTickRate * 0.8) {
health.status = 'degraded';
res.status(503).json(health);
return;
}
res.json(health);
});
app.get('/metrics', async (req, res) => {
// Prometheus-compatible metrics
res.set('Content-Type', 'text/plain');
res.send(await metrics.toPrometheusFormat());
});
app.listen(8080, () => {
console.log('Health endpoint attivo su :8080');
});
En İyi Uygulamalar ve Anti-Kalıplar
En İyi Uygulamalar
- Filoyu önceden ısıtın: Yoğunluk sırasında soğuk başlatmayı önlemek için her zaman bir HAZIR sunucu arabelleği bulundurun
- Geri dönüşlü Spot Bulut Sunucuları: Maliyetleri azaltmak için Spot'u kullanın ancak geri dönüş olarak %10-20 İsteğe Bağlı'yı koruyun
- Zarif kapatma: Kapanmadan önce sunuculara mevcut oyunları tamamlamaları için 30-60 saniye verin
- Kalıcı günlükler: Kapatmadan önce günlükleri S3'e veya merkezi bir sisteme yazın
- Görüntü sürümü oluşturma: Değiştirilemez etiketler kullanın (asla
latest) tekrarlanabilir dağıtımlar için - Bölgesel yakınlık: Oyuncuları gecikmenin en düşük olduğu bölgedeki sunuculara atayın
Kaçınılması Gereken Anti-Desenler
- Sunucudaki genel durum: Her oyun sunucusu altyapı açısından vatansız olmalıdır; oyun durumu oturumda yereldir
- Filo çok büyük veya çok küçük: Küçük filolar beklemeye neden oluyor; aşırı büyük atık para
- Çok agresif sağlık kontrolleri: Her saniye tüketilen kaynakları kontrol edin; 5-10 saniye genellikle yeterlidir
- Tahliye süresi yok: Boşaltma süresi olmayan bir sunucuyu öldürmek, devam eden oyunların aniden sona ermesine neden olur
Sonuçlar
Özel oyun sunucularının düzenlenmesi, arasında hassas bir denge gerektiren bir disiplindir. maliyetler, gecikme ve güvenilirlik. AWS GameLift karmaşıklığı azaltan yönetilen bir çözüm sunuyor Agones, çoklu bulut mimarileri ve ekipler için gereken esnekliği sunarken, güçlü Kubernetes uzmanlığı. Agones'un FleetIQ Adaptörü ile kombinasyonu muhtemelen Çoğu AAA oyunu için en iyi tercih: açık kaynak esnekliği ve tasarruf Spot Bulut Sunucularının maliyeti.
Serinin bir sonraki makalesinde nasıl sistem kurulacağını göreceğiz sofistike çöpçatanlık ELO ve Glicko-2 algoritmaları ile beceriye dayalı eşleştirmeyi orkestrasyon mekanizmalarıyla bütünleştiriyor bugün araştırdık.
Oyun Arka Uç Serisinde Gelecek Makaleler
- Madde 03: ELO ve Glicko-2 ile Eşleştirme Sistemi
- Madde 04: Gerçek Zamanlı Durum Senkronizasyonu ve Netcode Geri Alma
- Madde 05: Sunucu Tarafı Hile Önleme Mimarisi







