# 游戏服务器架构 ⭐ 核心竞争力

用生活化的比喻,让你从"知道有服务器"到"能设计支持万人在线的游戏服务器架构"

前置知识:第03章 Java NIO 与 Netty(网络通信)、第10章 高并发与分布式一致性


阅读指南(初学者必看)

为什么你需要学习游戏服务器架构?

这是从"Java 开发者"到"游戏架构师"的关键跨越。普通 Web 后端是请求-响应模式,游戏后端是长连接+有状态+实时同步——完全不同的架构思路。

学完本章,你能回答:

  • 房间服务器、网关服务器、战斗服务器各自的职责是什么?
  • 怎么设计支持 1000 并发的房间服务器?
  • 服务器怎么优雅地处理断线重连?
  • 有状态服务和无状态服务的区别?游戏服务器应该怎么选?
  • 架构怎么从单体演进到微服务?

本文结构

第一部分:游戏服务器 vs Web 服务器(理解本质区别)
第二部分:架构演进(从单体到微服务)
第三部分:房间服务器架构(最经典的游戏架构)
第四部分:网关服务器设计(流量入口)
第五部分:战斗服务器分离(有状态 vs 无状态)
第六部分:匹配服务与排行榜设计
第七部分:断线重连与会话恢复
第八部分:完整游戏服务器案例

一、游戏服务器 vs Web 服务器

生活类比:Web 服务器像"银行柜台"(来一个办一个),游戏服务器像"棋牌室"(一群人持续互动)。

Web 服务器 游戏服务器
连接模式 短连接(HTTP) 长连接(WebSocket/TCP)
状态 无状态 有状态(房间、战斗)
通信模式 请求-响应 推送+请求
并发模型 请求级 连接级(玩家级)
数据一致性 数据库保证 内存+定期持久化
延迟要求 < 500ms < 100ms(战斗 < 50ms)
典型 QPS 1 万/单机 1 万连接/单机

二、架构演进

阶段1:单体架构(MVP阶段,<1000人)

一台服务器
  ├─ 前端文件
  ├─ WebSocket游戏逻辑
  └─ 数据库

特点:简单、快速开发、先跑通!

适用:小游戏、Demo阶段、验证玩法。

阶段2:分离部署(100人-10000人)

前端服务(Nginx)
  ↓
游戏服(Netty)- DB
管理后台(SpringBoot)- DB

特点:游戏服和后台分离,可以独立部署和扩展。

阶段3:分布式架构(用户 < 100000)

┌─────────────────────────────────────────┐
│                  网关                    │
└─────────────┬─────────────────────────────┘
              │
    ┌─────────┼─────────┐
    ↓         ↓         ↓
┌───────┐ ┌───────┐ ┌───────┐
│房间服1│ │房间服2│ │房间服3│  ...
└───────┘ └───────┘ └───────┘

阶段4:微服务架构(1000人-10万人+)

┌─────────────────────────────────────────┐
│         接入层(Gateway)                 │
├─────────────────────────────────────────┤
│ 匹配服务  │ 房间服务  │  排行榜服务  │...│
├─────────────────────────────────────────┤
│          数据层(Redis/MySQL)           │
└─────────────────────────────────────────┘

特点:每个服务独立部署、独立扩展、技术栈灵活!

架构选择原则

用户规模 架构 推荐技术
<1000 单机 原生Socket
<10000 分离 Netty
<100000 分布式 微服务
100000+ 云原生 K8s + 微服务

三、游戏服务器整体架构

                 玩家客户端
                     |
                     v
+------------------------------------------+
|              网关服务器 (Gateway)          |
|  - 连接管理(维持TCP长连接)                |
|  - 消息路由(转发到对应服务)                |
|  - 心跳检测(踢掉死连接)                   |
|  - 协议编解码                               |
+------------------------------------------+
         |                    |
         v                    v
+---------------+    +-------------------+
|   登录服务器   |    |   匹配服务器       |
+---------------+    +-------------------+
                              |
                              v
+------------------------------------------+
|              房间服务器 (Room)             |
|  - 创建/加入/离开/销毁房间                 |
|  - 房间内消息广播                          |
|  - 游戏状态同步                            |
+------------------------------------------+
                              |
                              v
+------------------------------------------+
|              战斗服务器 (Battle)           |
|  - 帧同步/状态同步                         |
|  - 战斗计算                                |
|  - 伤害判定                                |
+------------------------------------------+

分层架构设计

┌─────────────────────────────────────────────────┐
│                   客户端层                        │
│              (H5/小程序/APP)                     │
└─────────────────────────┬───────────────────────┘
                          │
┌─────────────────────────┴───────────────────────┐
│                   网关层                         │
│   - 协议转换(HTTP↔Socket)                      │
│   - 统一鉴权                                      │
│   - 限流熔断                                      │
│   - 路由分发                                      │
└─────────────────────────┬───────────────────────┘
                          │
┌─────────────────────────┴───────────────────────┐
│                   业务逻辑层                      │
│   ┌─────────┐ ┌─────────┐ ┌─────────┐           │
│   │ 用户服务 │ │ 房间服务 │ │ 匹配服务 │           │
│   └─────────┘ └─────────┘ └─────────┘           │
└─────────────────────────┬───────────────────────┘
                          │
┌─────────────────────────┴───────────────────────┐
│                    数据层                        │
│   ┌─────────┐ ┌─────────┐ ┌─────────┐           │
│   │ MySQL  │ │  Redis  │ │  Kafka  │           │
│   └─────────┘ └─────────┘ └─────────┘           │
└─────────────────────────────────────────────────┘

四、房间服务器架构

生活类比:房间服务器就像"KTV 包间管理"——管理包间的创建、加入、使用、销毁。

整体架构

玩家 → 网关服务器 → 房间服务器集群
                   ├── Room Server 1(房间 1~100)
                   ├── Room Server 2(房间 101~200)
                   └── Room Server 3(房间 201~300)
                         │
                         ├── Redis(房间状态缓存)
                         ├── MySQL(持久化)
                         └── MQ(事件通知)

房间生命周期

创建 → 等待 → 准备 → 游戏中 → 结算 → 销毁
  │       │       │       │        │       │
  │    玩家加入  全部准备  游戏逻辑  计算结果  释放资源
  └──────────────────────────────────────────┘

房间状态机

                    ┌─────────────┐
                    │   WAITING   │  等待玩家
                    └──────┬──────┘
                           │ 玩家都准备好了
                           ↓
                    ┌─────────────┐
              ┌─────│  LOADING    │  加载资源
              │     └──────┬──────┘
              │            │ 加载完成
    有人掉线   │            ↓
              │     ┌─────────────┐
              └────→│  PLAYING    │  游戏进行中
                    └──────┬──────┘
                           │ 游戏结束
                           ↓
                    ┌─────────────┐
                    │   ENDED     │  结算
                    └──────┬──────┘
                           │ 重新开始或解散
                           ↓
                    ┌─────────────┐
                    │  DESTROYED  │  销毁
                    └─────────────┘

房间服务器核心代码

@Service
public class RoomService {
    private final Map<String, Room> rooms = new ConcurrentHashMap<>();
    
    // 创建房间
    public Room createRoom(Player creator, int maxPlayers) {
        String roomId = generateRoomId();
        Room room = new Room(roomId, maxPlayers);
        room.addPlayer(creator);
        rooms.put(roomId, room);
        
        // 通知匹配服务
        eventPublisher.publish(new RoomCreatedEvent(roomId, maxPlayers));
        return room;
    }
    
    // 加入房间
    public Room joinRoom(String roomId, Player player) {
        Room room = rooms.get(roomId);
        if (room == null) throw new RoomNotFoundException();
        if (room.isFull()) throw new RoomFullException();
        if (room.getStatus() != RoomStatus.WAITING) throw new GameAlreadyStartedException();
        
        room.addPlayer(player);
        
        // 通知房间内所有人
        broadcast(roomId, new PlayerJoinedMessage(player));
        return room;
    }
    
    // 开始游戏
    public void startGame(String roomId) {
        Room room = rooms.get(roomId);
        if (room.getPlayers().size() < room.getMinPlayers()) {
            throw new NotEnoughPlayersException();
        }
        
        room.setStatus(RoomStatus.PLAYING);
        
        // 初始化战斗状态
        BattleState battleState = battleService.initBattle(room.getPlayers());
        room.setBattleState(battleState);
        
        broadcast(roomId, new GameStartMessage(battleState));
    }
    
    // 销毁房间
    public void destroyRoom(String roomId) {
        Room room = rooms.remove(roomId);
        if (room != null) {
            // 保存战斗记录
            battleRecordService.save(room.getBattleState());
            // 通知匹配服务
            eventPublisher.publish(new RoomDestroyedEvent(roomId));
        }
    }
}

房间内广播

public void broadcastToRoom(long roomId, GameMessage msg) {
    Room room = RoomManager.getRoom(roomId);
    if (room == null) return;
    
    for (Player player : room.getPlayers().values()) {
        Channel channel = player.getChannel();
        if (channel != null && channel.isActive()) {
            channel.writeAndFlush(msg);
        }
    }
}

五、网关服务器设计

生活类比:网关就像"酒店前台"——所有客人先到前台,前台根据需求分配到不同的楼层(服务)。

网关六大职责

// 1. 连接管理:维护玩家 WebSocket/TCP 连接
public class ConnectionManager {
    private final Map<String, GameSession> sessions = new ConcurrentHashMap<>();
    
    public void onConnect(Channel channel) {
        GameSession session = new GameSession(channel);
        sessions.put(session.getSessionId(), session);
    }
    
    public void onDisconnect(String sessionId) {
        GameSession session = sessions.remove(sessionId);
        if (session != null) {
            // 通知房间服务玩家断线
            roomService.playerDisconnected(session.getPlayerId());
        }
    }
}

// 2. 会话保持:玩家断线重连后恢复会话
// 3. 消息路由:根据消息类型分发到不同服务
// 4. 负载均衡:新连接分配到负载最低的房间服务器
// 5. 心跳检测:检测死连接,及时清理
// 6. 防攻击:连接频率限制、消息频率限制

消息路由

客户端消息 → 网关
  │
  ├── 登录/注册 → 认证服务
  ├── 匹配请求 → 匹配服务
  ├── 房间操作 → 房间服务
  ├── 战斗操作 → 战斗服务
  ├── 聊天消息 → 聊天服务
  └── 支付请求 → 支付服务

网关核心功能实现

@Component
public class GatewayHandler extends ChannelInboundHandlerAdapter {
    // playerId -> Channel
    private static Map<Long, Channel> sessions = new ConcurrentHashMap<>();

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, GameMessage msg) {
        // 1. 鉴权
        if (!verifyToken(msg.getToken())) {
            ctx.writeAndFlush(buildError(401, "未登录"));
            return;
        }

        // 2. 限流
        if (!checkRateLimit(msg.getPlayerId(), msg.getMsgId())) {
            ctx.writeAndFlush(buildError(429, "请求太频繁"));
            return;
        }

        // 3. 路由
        Route targetServer = route(msg);
        forwardToServer(ctx, msg, targetServer);
    }

    private boolean verifyToken(String token) {
        return redisTemplate.hasKey("session:" + token);
    }

    private boolean checkRateLimit(long playerId, int msgId) {
        String key = "ratelimit:" + playerId + ":" + msgId;
        Long count = redisTemplate.opsForValue().increment(key);
        if (count == 1) {
            redisTemplate.expire(key, 1, TimeUnit.MINUTES);
        }
        return count <= 100; // 每分钟最多100次
    }

    private Route route(GameMessage msg) {
        switch (msg.getMsgId()) {
            case MSG_ROOM_CREATE:
            case MSG_ROOM_JOIN:
                return roomService;
            case MSG_MATCH_START:
                return matchService;
            default:
                return chatService;
        }
    }
}

六、战斗服务器分离

有状态 vs 无状态设计

无状态设计(适合回合制/休闲游戏):
  战斗服务器不存储玩家持久数据
  战斗开始 → 从数据库加载 → 战斗逻辑 → 结果写入数据库 → 释放

  优点:可以随意扩缩容、重启不影响
  缺点:每帧需要从外部获取状态(延迟高)

有状态设计(适合 MOBA/FPS 等实时游戏):
  战斗服务器持有战斗状态
  状态在内存中,操作直接读写内存

  优点:延迟最低、逻辑简单
  缺点:扩缩容困难、重启丢失状态、需要断线重连
有状态服务 无状态服务
特点 保存玩家游戏状态 不保存状态
例子 战斗服务器、房间服务器 登录服务器、排行榜
扩展 难(需要迁移状态) 容易(任意实例处理)
部署 需要粘性会话 负载均衡即可

战斗状态管理

// 有状态战斗服务
@Service
public class BattleService {
    private final Map<String, BattleRoom> activeBattles = new ConcurrentHashMap<>();
    
    // 战斗主循环(固定 20Hz)
    @Scheduled(fixedRate = 50)
    public void gameLoop() {
        for (BattleRoom battle : activeBattles.values()) {
            battle.tick();  // 逻辑帧
        }
    }
    
    // 处理玩家操作
    public void handleAction(String battleId, long playerId, BattleAction action) {
        BattleRoom battle = activeBattles.get(battleId);
        if (battle == null) return;
        
        battle.processAction(playerId, action);
        
        // 广播状态给所有玩家
        broadcast(battleId, battle.getCurrentState());
    }
}

战斗服务器设计

public class BattleServer {
    // 战斗房间映射到具体服务器
    private Map<Long, String> battleRoomMap = new ConcurrentHashMap<>();
    
    // 分配战斗服务器
    public String assignBattleServer(long roomId) {
        // 选择负载最低的服务器
        List<String> servers = discovery.getActiveBattleServers();
        String server = selectLeastLoaded(servers);
        battleRoomMap.put(roomId, server);
        return server;
    }
    
    // 帧同步:每50ms广播一次帧
    public void frameSync(long roomId) {
        Frame frame = generateFrame(roomId);
        broadcastToRoom(roomId, frame);
    }
}

七、匹配服务设计

匹配算法

@Service
public class MatchService {
    // 等待队列(按elo分数分组)
    private final Map<Integer, ConcurrentLinkedQueue<Player>> eloQueues = new ConcurrentHashMap<>();

    // 玩家匹配信息
    private final Map<Long, MatchInfo> playerMatches = new ConcurrentHashMap<>();

    // 入队
    public void joinQueue(Long playerId, int elo) {
        if (playerMatches.containsKey(playerId)) {
            throw new GameException("已在匹配中");
        }

        int eloBucket = elo / 50 * 50;
        ConcurrentLinkedQueue<Player> queue = eloQueues.computeIfAbsent(eloBucket,
            k -> new ConcurrentLinkedQueue<>());

        queue.offer(new Player(playerId, elo));
        playerMatches.put(playerId, new MatchInfo(eloBucket, System.currentTimeMillis()));

        tryMatch(eloBucket);
    }

    // 匹配
    private void tryMatch(int eloBucket) {
        ConcurrentLinkedQueue<Player> queue = eloQueues.get(eloBucket);
        if (queue == null || queue.size() < 2) {
            return;
        }

        Player p1 = queue.poll();
        Player p2 = queue.poll();

        if (p1 == null || p2 == null) {
            return;
        }

        playerMatches.remove(p1.getId());
        playerMatches.remove(p2.getId());

        Room room = roomService.createRoom(p1.getId(), new RoomConfig(2));
        roomService.joinRoom(room.getId(), p2.getId());
    }

    // 定时清理超时匹配(30秒超时)
    @Scheduled(fixedRate = 5000)
    public void cleanupTimeout() {
        long now = System.currentTimeMillis();
        playerMatches.entrySet().removeIf(entry -> {
            if (now - entry.getValue().getJoinTime() > 30000) {
                leaveQueue(entry.getKey());
                return true;
            }
            return false;
        });
    }
}

MMR(Match Making Rating)匹配

@Service
public class MMRMatchService {
    private static final int ELO_RANGE = 100;  // ±100分

    public List<Player> findMatch(Long playerId, int elo, int targetCount) {
        List<Player> matched = new ArrayList<>();
        matched.add(new Player(playerId, elo));

        // 从近到远扩散搜索
        for (int offset = 0; offset <= 500; offset += ELO_RANGE) {
            List<Player> candidates = findCandidatesInRange(elo - offset, elo + offset);
            candidates.removeIf(p -> p.getId().equals(playerId));

            for (Player candidate : candidates) {
                if (matched.size() >= targetCount) {
                    return matched;
                }
                if (isCompatible(elo, candidate.getElo())) {
                    matched.add(candidate);
                }
            }

            if (matched.size() >= targetCount) {
                break;
            }
        }

        return matched.size() >= 2 ? matched : Collections.emptyList();
    }

    private boolean isCompatible(int elo1, int elo2) {
        return Math.abs(elo1 - elo2) <= ELO_RANGE;
    }
}

八、排行榜服务设计

实时排行榜

@Service
public class RankService {
    private static final String RANK_KEY_PREFIX = "rank:";

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 更新排行榜
    public void updateScore(Long playerId, String rankType, long score) {
        String rankKey = RANK_KEY_PREFIX + rankType;
        redisTemplate.opsForZSet().add(rankKey, playerId.toString(), score);

        // 异步更新MySQL(降级处理)
        CompletableFuture.runAsync(() -> {
            playerRepository.updateScore(playerId, rankType, score);
        });
    }

    // 获取玩家排名
    public RankInfo getRank(Long playerId, String rankType) {
        String rankKey = RANK_KEY_PREFIX + rankType;
        Long rank = redisTemplate.opsForZSet().reverseRank(rankKey, playerId.toString());
        Double score = redisTemplate.opsForZSet().score(rankKey, playerId.toString());

        return new RankInfo(
            rank != null ? rank + 1 : null,
            score != null ? score.longValue() : 0
        );
    }

    // 获取TOP N
    public List<RankItem> getTopN(String rankType, int n) {
        String rankKey = RANK_KEY_PREFIX + rankType;
        Set<Object> top = redisTemplate.opsForZSet().reverseRange(rankKey, 0, n - 1);

        List<RankItem> result = new ArrayList<>();
        int rank = 1;
        for (Object playerIdObj : top) {
            String playerId = playerIdObj.toString();
            Double score = redisTemplate.opsForZSet().score(rankKey, playerId);
            Player player = playerRepository.findById(Long.parseLong(playerId));

            result.add(new RankItem(
                rank++,
                Long.parseLong(playerId),
                player.getName(),
                score != null ? score.longValue() : 0
            ));
        }
        return result;
    }
}

九、断线重连与会话恢复

生活类比:断线重连就像"电话掉线后回拨"——你需要知道刚才聊到哪了,从那里继续。

断线重连流程

1. 客户端检测断线
   ↓
2. 指数退避重试连接(1s → 2s → 4s → 8s → 最大30s)
   ↓
3. 重连请求(携带 sessionId + lastFrame)
   ↓
4. 网关查找会话
   ├── 会话存在 → 恢复连接 → 补发缺失帧
   └── 会话超时 → 需要重新登录
   ↓
5. 恢复游戏

会话恢复代码

public class ReconnectService {
    // 会话保留时间:5 分钟
    private static final long SESSION_TIMEOUT = 5 * 60 * 1000;
    
    public ReconnectResult reconnect(String sessionId, long playerId, int lastFrame) {
        // 1. 查找会话
        GameSession session = sessionManager.get(sessionId);
        if (session == null) {
            return ReconnectResult.expired();
        }
        
        // 2. 检查超时
        if (session.isExpired(SESSION_TIMEOUT)) {
            sessionManager.remove(sessionId);
            return ReconnectResult.expired();
        }
        
        // 3. 恢复连接
        session.setChannel(currentChannel);
        session.updateLastActive();
        
        // 4. 补发缺失帧
        BattleRoom battle = battleService.getBattle(session.getBattleId());
        List<BattleState> missedFrames = battle.getFramesAfter(lastFrame);
        
        return ReconnectResult.success(missedFrames);
    }
}

十、跨服通信

服务间通信方案

方案 适用场景
HTTP/REST 配置同步、低频调用
gRPC 高性能服务间调用
Redis Pub/Sub 广播消息、状态通知
MQ (RocketMQ/Kafka) 异步解耦、削峰填谷

使用 Redis 做跨服消息

// 服务器A发布消息
redis.publish("cross:server:room_100", "player_join:10086");

// 服务器B订阅消息
redis.subscribe(new JedisPubSub() {
    public void onMessage(String channel, String message) {
        // 处理跨服消息
    }
}, "cross:server:room_100");

十一、完整项目结构

game-server/
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── game/
│   │   │           ├── GameApplication.java
│   │   │           │
│   │   │           ├── gateway/           # 网关
│   │   │           │   ├── GatewayHandler.java
│   │   │           │   ├── RouteService.java
│   │   │           │   └── RateLimiter.java
│   │   │           │
│   │   │           ├── user/              # 用户服务
│   │   │           │   ├── UserController.java
│   │   │           │   ├── UserService.java
│   │   │           │   └── UserRepository.java
│   │   │           │
│   │   │           ├── room/              # 房间服务
│   │   │           │   ├── RoomController.java
│   │   │           │   ├── RoomService.java
│   │   │           │   ├── Room.java
│   │   │           │   └── MatchService.java
│   │   │           │
│   │   │           ├── rank/              # 排行榜服务
│   │   │           │   ├── RankController.java
│   │   │           │   ├── RankService.java
│   │   │           │   └── RankPersistService.java
│   │   │           │
│   │   │           ├── common/            # 公共模块
│   │   │           │   ├── GameMessage.java
│   │   │           │   ├── GameException.java
│   │   │           │   ├── Result.java
│   │   │           │   └── Constants.java
│   │   │           │
│   │   │           └── config/            # 配置
│   │   │               ├── RedisConfig.java
│   │   │               ├── NettyConfig.java
│   │   │               └── WebSocketConfig.java
│   │   │
│   │   └── resources/
│   │       └── application.yml
│   │
│   └── test/
│       └── java/
│           └── com/
│               └── game/
│                   ├── RoomServiceTest.java
│                   └── RankServiceTest.java
│
└── docker-compose.yml

十二、性能优化要点

支持 1000 并发的关键设计

优化点 实现
连接管理 Netty NIO + 单线程多连接
房间隔离 ConcurrentHashMap + 细粒度锁
消息广播 只发给在线且active的channel
协议编解码 自定义二进制协议,比JSON省50%+带宽
排行榜 Redis ZSet,O(logN)更新
背包 本地缓存 + 定时持久化

压测指标

# 使用wrk或自定义压测工具
# 目标:1000并发连接,每个房间4人

# 压测结果参考:
# - 单服务器支持:1000~3000并发
# - 消息延迟:P99 < 50ms
# - 内存占用:每连接约10KB,1000连接约10MB
# - CPU:4核可支撑,8核更稳

十三、自问自答

Q1:游戏服务器一台能支持多少在线?

取决于游戏类型。休闲游戏(少量消息):12 万连接/单机。实时对战(高频同步):30005000 连接/单机。核心瓶颈是 CPU(逻辑计算)和带宽(状态同步)。

Q2:房间服务器和战斗服务器一定要分开吗?

不一定。小项目可以合并。大项目分开是为了独立扩缩——匹配服务无状态可以随意扩,战斗服务有状态需要特殊处理。

Q3:网关单点怎么办?

网关无状态,可以水平扩展。用 Nginx/HAProxy 做负载均衡,客户端连接时随机分配到不同网关实例。

Q4:战斗中服务器挂了怎么办?

这是游戏最大的痛点。方案:1. 战斗状态实时备份到备用节点;2. 玩家断线重连到新节点后从备份恢复;3. 最坏情况:战斗作废,补偿玩家。

Q5:怎么从单机架构演进到分布式?

先单体 → 网关分离 → 匹配服务分离 → 战斗服务分离 → 数据库分库分表。每一步都是渐进的,不要一步到位。

Q6:网关服务器可以用 Nginx 代替吗?

游戏用 TCP 长连接,Nginx 主要支持 HTTP/WebSocket。自定义二进制协议需要 Netty 自己实现网关。

Q7:房间服务器有状态怎么扩展?

用一致性哈希。相同 roomId 总是路由到同一台服务器。新增节点只影响部分房间迁移。

Q8:帧同步和状态同步怎么选?

RTS/格斗用帧同步(延迟低,一致性好)。MOBA/RPG 用状态同步(带宽低,反作弊好)。


实践任务

  1. 实现房间服务器:创建/加入/退出/销毁,支持 100 并发
  2. 实现网关服务器:基于 Netty 的 WebSocket 网关,支持消息路由
  3. 实现断线重连:客户端模拟断线,5 秒内重连恢复游戏
  4. 实现匹配服务:Elo 匹配算法,支持多段位匹配
  5. 实现排行榜:Redis Sorted Set,支持 Top N 和附近排名
  6. 压测:模拟 1000 并发创建房间,观察服务器 CPU 和内存
  7. 画架构图:为一个 MOBA 游戏画出完整的服务器架构图

与其他章节的关联

本节内容 关联章节 关联点
Netty 网关 第03章 Java NIO 与 Netty 网关基于 Netty 实现
分布式一致性 第10章 高并发与分布式一致性 有状态服务的容灾
协议设计 第12章 游戏协议设计与优化 网关的消息协议
实时通信 第14章 游戏实时通信优化 WebSocket 优化
容器化 第15章 云原生与容器化 服务器 K8s 部署
网络同步 4_1 第02~05章 帧同步/状态同步的服务端实现

⬅️ 上一章:高并发与分布式一致性 | ➡️ 下一章:游戏协议设计与优化