凌峰创科服务平台

Firefly游戏服务器核心架构如何实现?

Firefly 游戏服务器学习笔记

Firefly 是什么?

Firefly 是一个用 Go 语言(Golang)编写的开源、高性能、分布式游戏服务器框架,它旨在简化游戏服务器的开发,让开发者可以专注于游戏逻辑本身,而不是底层网络、并发、数据存储等复杂问题。

Firefly游戏服务器核心架构如何实现?-图1
(图片来源网络,侵删)

核心特点:

  • 高性能: 基于 Go 的 Goroutine 和 Channel 模型,轻松实现高并发,非常适合处理大量客户端连接。
  • 分布式: 天然支持分布式部署,通过 Gate(网关)和 Hall(大厅/逻辑服)的分离,可以水平扩展,承载海量玩家。
  • 组件化: 框架由多个独立的、可插拔的组件构成(如网络、数据库、日志、RPC等),开发者可以根据需求选择和替换。
  • 热更新: 支持在不重启服务器的情况下,更新游戏逻辑代码,对于需要持续运营的游戏至关重要。
  • 协议友好: 默认支持多种协议(如 TCP、WebSocket、自定义二进制协议),并内置了 Protobuf 的支持,方便进行数据序列化和反序列化。
  • 文档与社区: 拥有相对完善的文档和活跃的社区(主要在 QQ 群和 Gitee),遇到问题容易找到帮助。

核心架构设计

理解 Firefly 的架构是学习的第一步,它的设计非常清晰,主要分为以下几个层次和角色:

架构分层

  • Gate (网关服):

    • 职责: 负责接收所有玩家的网络连接,它是一个“无状态”的服务,只负责网络数据的收发和转发。
    • 工作流程:
      1. 玩家客户端连接到 Gate。
      2. Gate 接收玩家的登录请求,验证身份。
      3. 验证通过后,Gate 将该玩家的连接与一个指定的 Hall 服(逻辑服)绑定。
      4. 之后,该玩家的所有游戏消息都通过 Gate 转发到绑定的 Hall 服,Hall 服的返回消息也通过 Gate 转发回客户端。
    • 优势: Gate 可以无脑地水平扩展,玩家可以随机或根据哈希算法连接到不同的 Gate,再由 Gate 路由到对应的 Hall,这解决了单点登录和连接数瓶颈的问题。
  • Hall (大厅/逻辑服):

    Firefly游戏服务器核心架构如何实现?-图2
    (图片来源网络,侵删)
    • 职责: 游戏的核心逻辑所在地,一个游戏世界或一个逻辑区域通常由一个或多个 Hall 服组成。
    • 工作流程:
      1. 接收来自 Gate 转发过来的玩家消息。
      2. 执行具体的游戏逻辑(如移动、战斗、聊天等)。
      3. 与其他 Hall 服或数据库进行交互。
      4. 将处理结果(如位置更新、战斗结果)通过 Gate 发送给相关玩家。
    • 状态: Hall 服是“有状态”的,它维护了其所负责玩家的游戏状态。
  • DB (数据库服):

    • 职责: 负责数据持久化,可以是 MySQL、MongoDB 等,Hall 服通过 RPC 调用 DB 服来读写数据。
    • 优势: 数据库服务独立部署,可以单独进行优化和维护,不影响游戏逻辑服务的运行。
  • Agent (代理服):

    • 职责: 这是一个可选的组件,通常用于处理一些跨服逻辑、排行榜、公会管理等需要全局访问但又不想放在单个 Hall 服中的功能,它也通过 RPC 与其他服务通信。

关键技术组件

  • 模块: Firefly 的核心是“模块化”,每个服务(Gate, Hall, DB)都是一个独立的进程,由一个或多个模块组成,模块之间通过 module.Module 接口进行解耦。
  • 组件: 组件是模块的依赖项,如网络组件、日志组件、数据库组件、RPC 组件等,Firefly 提供了一套依赖注入机制,让模块可以方便地使用这些组件。
  • 消息路由: Firefly 使用一个中心化的 dispatcher 来处理消息,当一个模块收到消息后,会根据消息的 ID 注册一个处理函数,当该 ID 的消息到来时,对应的处理函数就会被自动调用,这使得消息处理逻辑非常清晰。
  • RPC (Remote Procedure Call): Firefly 使用 gRPC 作为其默认的 RPC 框架,用于服务间的通信(如 Hall 调用 DB),这使得跨进程的服务调用就像调用本地函数一样简单。

环境搭建与第一个 "Hello World"

环境要求

  • Go 1.16+
  • Git
  • Protocol Buffers 编译器 (protoc)
  • Make (可选,但推荐)

安装步骤

# 1. 克隆 Firefly 仓库
git clone https://github.com/firefly-zero/firefly-zero.git
cd firefly-zero
# 2. 安装依赖
make dep
# 3. 编译所有工具和示例
make all

编译成功后,可执行文件会在 bin 目录下。

运行第一个示例

Firefly 自带了许多示例,helloworld 是最好的起点。

Firefly游戏服务器核心架构如何实现?-图3
(图片来源网络,侵删)
# 1. 启动一个简单的 Hall 服
# 这个示例中,Gate 和 Hall 在同一个进程里
bin/hall -f=hall/hall.conf
# 2. 在另一个终端,启动一个 Gate 服
# 同样,这个示例将 Gate 和 Hall 合二为一
bin/gate -f=gate/gate.conf

当你看到日志打印出服务启动成功后,一个最基础的 Firefly 游戏服务器就跑起来了,你可以使用 telnetnetcat 连接到 Gate 的端口,发送一个预定义的 protobuf 消息来测试。

核心概念深入

消息定义与处理

  • 定义消息: 使用 .proto 文件定义你的消息结构。

    syntax = "proto3";
    package message;
    message HelloMessage {
        string content = 1;
    }
  • 生成 Go 代码:

    protoc --go_out=. --go_opt=paths=source_relative \
           --go-grpc_out=. --go-grpc_opt=paths=source_relative \
           your_message.proto
  • 注册和处理消息: 在你的模块中,你需要告诉 Firefly 如何处理某个消息 ID。

    // 在模块的 Init 函数中注册
    func (m *YourModule) Init() {
        // 注册处理函数,messageID.HelloMessage 对应 .proto 文件中定义的消息 ID
        m.RegisterHandler(messageID.HelloMessage, m.HandleHello)
    }
    // 处理函数
    func (m *YourModule) HandleHello(session znet.Session, msg proto.Message) {
        helloMsg := msg.(*message.HelloMessage)
        log.Infof("Received hello message: %s", helloMsg.Content)
        // 可以给客户端回一个消息
        response := &message.HelloResponse{Content: "Server received your message!"}
        session.Send(messageID.HelloResponse, response)
    }

模块与生命周期

每个服务(进程)都是由一个或多个模块组成的,模块有清晰的声明周期:

  • Init(): 模块初始化时调用,通常用于注册消息处理器、加载配置、启动协程等。
  • Run(): Init 之后调用,模块的主循环,对于大多数模块,这个函数可以留空,因为 Firefly 的网络模型是事件驱动的,不需要你写 for 循环。
  • OnDestroy(): 服务关闭时调用,用于资源清理。

配置系统

Firefly 使用 ini 格式的配置文件,配置项通过结构体标签与代码绑定,非常方便。

type HallConfig struct {
    ListenAddr string `ini:"listen_addr"` // 监听地址
    DBAddr     string `ini:"db_addr"`     // 数据库地址
}
// 在模块中加载配置
var hallConf HallConfig
err := conf.Get("hall", &hallConf)

数据库操作

Firefly 封装了常用的数据库驱动,操作起来非常方便。

// 1. 在模块中获取数据库组件
db := m.GetModule("db").(*dbcomponent.DBComponent)
// 2. 执行查询
rows, err := db.Query("SELECT id, name FROM users WHERE id = ?", playerId)
if err != nil {
    // handle error
}
defer rows.Close()
// 3. 遍历结果
for rows.Next() {
    var id int64
    var name string
    if err := rows.Scan(&id, &name); err != nil {
        // handle error
    }
    log.Infof("User: %d, %s", id, name)
}
// 4. 执行更新
result, err := db.Exec("UPDATE users SET name = ? WHERE id = ?", newName, playerId)
if err != nil {
    // handle error
}
affected, _ := result.RowsAffected()
log.Infof("Updated %d rows", affected)

实战:开发一个简单的房间服务

假设我们要实现一个功能:玩家可以创建房间,其他玩家可以加入房间,房间内可以聊天。

  1. 定义消息:

    • CreateRoomReq: { player_id, room_name }
    • CreateRoomRes: { success, room_id, error_msg }
    • JoinRoomReq: { player_id, room_id }
    • JoinRoomRes: { success, error_msg }
    • ChatMsg: { player_name, content }
  2. 创建 RoomModule:

    • Hall 服务中新建一个 RoomModule
    • Init() 中注册 CreateRoomReq, JoinRoomReq, ChatMsg 的处理器。
  3. 实现逻辑:

    • HandleCreateRoom: 创建一个 Room 结构体实例,将其存入一个全局的 map[int64]*Room 中(room_id 为 key),并返回 CreateRoomRes
    • HandleJoinRoom: 根据 room_idmap 中找到房间,将玩家加入房间的玩家列表,并返回 JoinRoomRes
    • HandleChatMsg: 获取消息发送者的信息,遍历房间内所有玩家的 Session,将聊天消息广播给他们。
  4. 部署与测试:

    • 编译你的 Hall 服务,确保 RoomModule 被正确加载。
    • 启动 GateHall 服务。
    • 编写一个简单的客户端或使用 Postman 等工具,按顺序发送 CreateRoomReq, JoinRoomReq, ChatMsg 来验证功能。

学习路径与资源

  1. 从官方文档开始: Firefly 官方文档 是最好的入门材料。
  2. 阅读官方示例: examples 目录下的代码是最好的老师,特别是 helloworld, chat, area 等示例,涵盖了从简单到复杂的各种场景。
  3. 理解 Go 语言基础: Firefly 是 Go 写的,深入理解 Goroutine, Channel, Interface, Struct Tag 等是必须的。
  4. 学习 gRPC 和 Protobuf: Firefly 的服务间通信和数据序列化严重依赖这两者。
  5. 阅读源码: 当你对框架有基本了解后,可以尝试阅读 firefly 目录下的核心源码,module, net, container 等包,能让你更深刻地理解其设计思想。

总结与思考

Firefly 是一个非常优秀且现代的游戏服务器框架,它的 Go 语言基础和分布式架构使其非常适合开发中大型、高并发的在线游戏。

优点:

  • 开发效率高: 事件驱动和模块化设计让代码结构清晰,迭代快速。
  • 性能强大: Go 的并发模型能轻松应对数万甚至数十万的在线连接。
  • 扩展性好: 分布式架构支持服务的水平扩展,是应对未来用户增长的有力保障。
  • 社区活跃: 对于国内开发者来说,Gitee 上的社区和 QQ 群提供了很好的支持。

挑战:

  • 学习曲线: 对于没有 Go 语言和分布式系统经验的开发者来说,需要投入一定时间学习。
  • 文档细节: 虽然文档全面,但某些细节和最佳实践可能需要通过阅读源码和社区讨论来挖掘。
  • 生态: 相较于一些老牌框架(如 Pomelo, Netty),Firefly 的第三方插件和解决方案生态还在建设中。

建议:

从最简单的 helloworld 示例开始,逐步构建自己的小项目,在开发过程中,遇到问题多翻阅文档、源码和社区讨论,实践是掌握 Firefly 的最快途径,祝你学习顺利!

分享:
扫描分享到社交APP
上一篇
下一篇