QUICK START
五分钟跑通第一个请求
快速上手部分只做一件事:创建一个可被调用的顶层 Actor,然后依次从进程内、HTTP 和 AGP 入口访问它。三种入口最终复用同一张 Method Table。
初始化 Go Module,安装 ActorGo,并准备最小节点配置。
嵌入 Base,声明 AliasID,在 OnInit 注册稳定 Method ID。
本地调用直接 Invoke;后台接口挂 HTTP;实时客户端使用 AGP。
mkdir actorgo-quickstart && cd actorgo-quickstart
go mod init example.com/actorgo-quickstart
go get github.com/actorgo-game/actorgo@latest建议的学习顺序:先完成 Actor + HTTP 的 JSON 请求,确认 Method ID、Handler 和响应链路正确,再接入 TCP / WebSocket 与 Protobuf 客户端。
开发环境与依赖边界
Go 1.26.1+
版本以仓库 go.mod 为准。先用 go version 确认工具链,再初始化 Module 并安装 ActorGo。
Protobuf 工具链
使用 PB 契约时准备 protoc 与 Go 插件;JSON 快速上手不依赖代码生成。
单机零中间件
Standalone 模式可直接运行 Actor、HTTP、TCP 与 WebSocket,不需要先启动 NATS。
按需启动集群依赖
切换 Cluster 后再准备 NATS 或 RabbitMQ,并单独配置 NATS / ETCD Discovery。
ADD AN ACTOR
新增第一个 Actor
顶层 Actor 是外部请求的业务边界。下面的示例定义 JSON 请求与响应,注册 Method ID 1001,并随应用启动。代码结构与当前 ActorGo API 保持一致。
package main
import (
cfacade "github.com/actorgo-game/actorgo/facade"
cactor "github.com/actorgo-game/actorgo/net/actor"
)
const HelloMethodID uint32 = 1001
type HelloRequest struct {
Name string `json:"name"`
}
type HelloResponse struct {
Greeting string `json:"greeting"`
}
type HelloActor struct { cactor.Base }
func (*HelloActor) AliasID() string { return "hello" }
func (a *HelloActor) OnInit() {
a.Methods().Register(HelloMethodID, a.hello)
}
func (a *HelloActor) hello(
_ *cfacade.RequestContext,
req *HelloRequest,
) (*HelloResponse, error) {
return &HelloResponse{Greeting: "Hello, " + req.Name}, nil
}{
"env": "local",
"debug": true,
"print_level": "debug",
"node": {
"3": [{
"node_id": "0.0.3.1",
"enabled": true,
"address": "127.0.0.1:9080"
}]
}
}package main
import "github.com/actorgo-game/actorgo"
func main() {
app := actorgo.Configure(
"profile.json",
"0.0.3.1",
actorgo.Standalone,
)
app.AddActors(&HelloActor{})
app.Startup()
}AliasID 是服务边界顶层 Actor 在启动时创建,方法会写入应用级 Method Table。
Method ID 必须稳定业务方法建议集中管理,编号大于 5,并纳入协议版本控制。
ACTOR HTTP
开放 Actor HTTP 接口
HTTP Actor 不需要重复编写 Controller。注册组件后,所有顶层 Actor 的业务方法统一通过 POST /actor/{methodID} 暴露,Body 仍在目标 Actor 的 Mailbox 中解码。
import httpactor "github.com/actorgo-game/actorgo/net/httpactor"
app := actorgo.Configure("profile.json", "0.0.3.1", actorgo.Standalone)
app.AddActors(&HelloActor{})
app.Register(httpactor.NewComponent("actor-api", app.Address()))
app.Startup()curl -X POST "http://127.0.0.1:9080/actor/1001" \
-H "Content-Type: application/json" \
-H "X-ActorGo-Timeout-Ms: 3000" \
-d '{"name":"developer"}'响应 Body 为业务 Response,并保持请求使用的 JSON 或 Protobuf 编码。
通知成功进入 Mailbox,不返回业务 Body。
参数、认证、超时和框架状态会映射为明确的 HTTP 状态码。
用于后台与服务集成:HTTP 适合管理后台、机器人、运维工具和内部服务;实时游戏长连接仍建议使用 AGP。
AGP CONNECTION
接入 TCP 与 WebSocket
AGP 连接器只负责连接、分帧和 Packet 传输,业务仍按 Method ID 分发到同一个 HelloActor。TCP 使用长度帧,WebSocket 的一条 Binary Message 对应一个 Packet。
import (
cfacade "github.com/actorgo-game/actorgo/facade"
"github.com/actorgo-game/actorgo/net/connector"
"github.com/actorgo-game/actorgo/net/parser"
)
app.Register(parser.New("client", []cfacade.IConnector{
connector.NewTCP(":9000"),
connector.NewWS(":9001"),
}))uint32 big-endian length + Packet PB原生客户端、稳定长连接1 Binary Message = 1 Packet浏览器与跨平台客户端JSON / Protobuf由每次调用的 Codec 决定客户端不提交 ActorPath客户端只发送 Method ID 与 Body,服务端保留路由控制权。
先定义协议,再写 Handler生产项目建议用 Protobuf 管理请求、响应与跨版本兼容。
CORE CONCEPTS
框架的四个核心对象
ActorGo 把业务状态、调用协议与基础设施分成清晰的边界。开发时先确定状态归属,再定义方法契约,最后选择入口和部署方式。
Actor
封装状态与行为,通过单一 Mailbox 串行处理消息。
Method Table
用稳定 Method ID 注册 Typed Handler,并自动定位顶层 Actor。
RequestContext
承载 Transport、Codec、Session、RequestID 与 Metadata。
Body Codec
JSON 与 Protobuf 始终同时注册,可按调用选择编码。
Transport标识 AGP、HTTP 或 Cluster 入口Codec决定本次业务 Body 使用 JSON 或 ProtobufSession保存 Sid、Uid、IP 和自定义 DataRequestID用于请求关联,Method ID 由 Message 持有Metadata跨入口透传调用元数据FRAMEWORK LAYERS
框架分层与能力类别
把 ActorGo 看成一组可以独立理解、组合使用的设计类别。它不是只提供 Actor 容器,而是覆盖接入、调用、分布式、基础设施和运行保障的完整服务端骨架。
Application + Actor System
Application 统一编排组件生命周期;Actor System 管理 Actor、路由、超时和事件订阅。
Top-level + Child Actor
顶层 Actor 作为服务边界,动态子 Actor 承载玩家、房间、战局等运行时状态。
Method ID + Typed Handler
稳定方法 ID 连接所有入口,运行前校验请求与响应类型,运行时直接分发。
AGP + HTTP Actor
TCP、WebSocket 与 HTTP 共享业务方法表,协议差异收敛在边界层。
Cluster + Discovery
跨节点传输与服务发现解耦,可分别选择 NATS、RabbitMQ、NATS Discovery 或 ETCD。
Component Ecosystem
数据库、缓存、HTTP、任务与诊断能力通过组件生命周期按需装配。
Deadline + Panic Isolation
超时贯穿调用链,Handler、事件和定时器异常被限制在当前工作单元。
Structured Log + Gops
慢 Handler 阈值、结构化日志、运行状态和 Go 进程诊断组成基础观测面。
ACTOR MODELING
Actor 状态与层级设计
Actor 的价值不是“每个对象一个 goroutine”,而是给可变状态指定唯一所有者。只有这个 Actor 能修改自己的状态,其他对象必须通过消息表达意图。
在应用启动时注册,是外部请求和跨节点调用的服务边界。它的 Method ID 会写入 Application 方法表。
- 路径
- NodeID.ActorID
- 生命周期
- 随节点启动和停止
- 适合
- Gate、Match、World、Center
由父 Actor 在运行时创建,只安装本地 Mailbox,不向外暴露方法表。首次路由可通过 OnFindChild 动态恢复。
- 路径
- NodeID.ActorID.ChildID
- 生命周期
- 由父 Actor 管理
- 适合
- Player、Room、Battle、Scene
开发者建模准则
围绕一致性边界拆分。需要原子更新的一组状态放在同一个 Actor;无需一致性的状态不要强行合并。
顶层 Actor 负责路由,子 Actor 负责实体状态。外部客户端不能传 target,服务端拥有最终路由权。
避免同步自调用。Actor 不能同步 Invoke 自己;父到子使用 InvokeChild / NotifyChild,避免重新排队到父 Mailbox。
让 Handler 足够短。串行模型消除了锁,但长时间阻塞会直接扩大同一 Actor 后续消息的排队时间。
游戏场景建模示例
不要按数据表机械拆 Actor,应从“谁拥有可变状态、哪些修改必须串行、哪些实体需要独立扩缩容”出发确定边界。
Match · Room · Player
Match Actor 负责匹配队列,Room / Battle Actor 拥有单局状态,Player 子 Actor 保存局内玩家快照。
World · Scene · Entity
World Actor 维护全局服务边界,Scene Actor 按区域拆分,Entity 子 Actor 承载玩家与 NPC 的动态状态。
Player · City · Alliance
围绕玩家、城池和联盟的一致性边界拆分 Actor;跨实体操作通过消息编排并设计幂等。
Lobby · Guild · Chat
大厅处理入口编排,Guild Actor 串行化公会状态,Chat Actor 按频道或房间隔离消息流。
MESSAGE RUNTIME
消息运行时与方法契约
AGP、HTTP、本地 Invoke 和集群消息最终都变成同一种内部 Message。运行时只在边界处做协议转换,Actor 内部始终处理 typed request。
单一调度循环Mailbox、Event 与 Timer 在同一个 Actor goroutine 中被 select 和串行执行。
进程内零编解码当 payload 已是目标 Go 类型时直接调用;只有网络字节才经过 Body Codec。
超时不阻塞 Actor结果通道带缓冲;即使调用方已经超时,Actor 完成工作后也能安全回收 Message。
Typed Handler 如何注册
所有顶层入口共用同一张方法表。注册时校验签名,重复 ID 或非法 Handler 会在 Actor 初始化阶段直接暴露。
const LoginMethodID uint32 = 1001
func (a *PlayerActor) OnInit() {
a.Methods().Register(LoginMethodID, a.Login)
}
func (a *PlayerActor) Login(
ctx *facade.RequestContext,
request *playerv1.LoginRequest,
) (*playerv1.LoginResponse, error) {
return &playerv1.LoginResponse{}, nil
}func(*RequestContext, *Request) (*Response, error)func(*RequestContext, *Request) errorPROTOCOL & ENTRY
协议、会话与调用入口
客户端只提交 Method ID 与 Body,不指定 Actor Target。外部请求先进入顶层 Actor,再由服务端决定是否访问动态子 Actor。
uint32 big-endian length + Packet PB长连接客户端一条 Binary Message 对应一个 Packet子协议 agp.v1POST /actor/{methodID}Request 200 · Notify 202Protobuf ClusterMessageInvoke / NotifyPOST /actor/1001 Content-Type: application/jsonRequest 成功返回 200;Notify 成功返回 202;错误 Body 统一为 HTTPError。
Session 是快照,不是共享对象
排队和跨节点时会复制 Sid、Uid、IP 与 Data map,避免传输层与 Actor 同时修改可变状态。
Codec 是逐调用属性
Protobuf 与 JSON 始终同时注册。Codec 为 0 时使用应用默认值;默认值只能在 Startup 前修改。
外部入口不接受 Target
客户端只提供 Method ID 与 Body。完整 ActorPath 只用于服务端内部访问动态子 Actor。
Deadline 贯穿调用链
RequestContext 包含 context.Context,取消和截止时间会跨 Mailbox 与集群边界继续生效。
AGP 必须遵守的协议约束
控制包固定 Protobuf
Packet 与 ClusterMessage 固定使用 Protobuf;JSON / PB 只决定业务 Body 的编解码。
三种业务消息
客户端交互只有 Request、Response、Notify;Request 返回结果,Notify 成功入队即结束。
业务 ID 必须大于 5
1–5 保留给 Handshake、Heartbeat、Cancel、GoAway 与 Kick,业务 Method ID 在节点内全局唯一。
WebSocket 只收 Binary
一条 Binary Message 对应一个 Packet,并声明 agp.v1 子协议;Text Message 会被拒绝。
EVENT, TIMER & CHILD
事件、定时器与子 Actor
方法调用解决明确的请求与通知;事件、定时器和子 Actor 则负责领域内的异步协作。三者都回到 Actor 自己的串行执行上下文,避免额外并发状态。
Event
按事件名订阅,可用 UniqueID 限定接收者。事件进入 Actor 自己的 Event 队列,并在 Actor goroutine 中串行处理。
适合领域内广播与状态联动Timer
基于全局分层时间轮触发,再把 timerID 投递回 Actor 队列。支持循环、单次、固定时刻和自定义 Schedule。
适合回合、超时、刷新与周期任务Child Actor
由父 Actor 管理动态生命周期;父级负责首次路由,也可在 OnFindChild 中按需恢复子 Actor。
适合玩家、房间、场景等动态实体async 参数要谨慎:定时器支持异步触发选项,但一旦业务函数脱离 Actor goroutine,就需要由开发者自己保证并发安全。
LOCAL & CLUSTER
集群、发现与远程调用
本节点使用 Invoke / Notify,跨节点使用 InvokeNode / NotifyNode。只有服务端内部定向访问子 Actor 时才使用完整 ActorPath。
ctx := facade.NewRequestContext(context.Background())
ctx.Codec = facade.CodecProtobuf
// 本节点顶层方法
result := app.ActorSystem().Invoke(ctx, methodID, req)
// 指定远端节点;底层可选择 NATS 或 RabbitMQ
result = app.ActorSystem().InvokeNode(
ctx, "center-1", methodID, req,
)默认选择,适合节点间低延迟 RPC,部署与使用更轻量。
需要统一 MQ 基础设施时可选,当前实现为尽力投递。
维护 NodeID、NodeType 与节点信息。可使用 NATS 或 ETCD,独立于消息传输选择。
业务根据服务类型和路由策略选择节点,再调用 InvokeNode / NotifyNode。
把 RequestContext、Method ID、Payload 与 Deadline 编码为 Protobuf ClusterMessage。
目标节点仍通过本地 Method Table 定位顶层 Actor,调用方不需要知道远端 ActorPath。
跨节点传输的实际语义
发现与传输独立
Discovery 可选择 NATS 或 ETCD;Cluster Transport 可独立选择 NATS 或 RabbitMQ。
请求响应可关联
Invoke 使用 Request / Reply;RabbitMQ 通过 ReplyTo 与 CorrelationId 关联响应并执行本地超时。
通知单向发布
Notify 不等待业务响应。RabbitMQ 直接发布到目标 remote routing key,不设置 ReplyTo。
明确背压与交付语义
工作队列满时 Request 返回 RESOURCE_EXHAUSTED、Notify 丢弃;当前 RabbitMQ 实现为尽力投递。
APPLICATION & COMPONENT
应用与组件生命周期
Application 是节点进程的组合根。组件按注册顺序启动、按逆序停止,使数据库、连接器、Actor System 等依赖关系可以确定性释放。
注入 Application
按注册顺序初始化
所有组件就绪后执行
逆序准备关闭
逆序释放资源
Profile 与节点
Profile 支持 include 合并、环境、日志级别、配置目录和节点设置。node_id 可写精确值、正则或列表,用一套配置描述同类型节点。
连接入口
TCP / WebSocket Connector、AGP Parser、HTTP Actor 与 Gin 都是边界适配器,业务 Actor 不感知监听器细节。
资源组件
GORM、Mongo、Redis 和 Data Config 在 Init 阶段建立资源,在逆序 OnStop 阶段释放,避免业务自行管理全局连接。
运行保障
Logger、Gops、Cron 和关闭钩子覆盖日志、诊断、计划任务与优雅停机,组件异常会被记录并尽量继续关闭流程。
INFRASTRUCTURE & PRODUCTION
基础设施与生产实践
Actor 模型减少了共享状态问题,但不会自动解决慢 I/O、重复消息、协议兼容和观测缺口。上线前应显式确认这些工程边界。
设置调用边界
为 Invoke 设置端到端 CallTimeout,并配置 ExecutionTimeout 记录慢 Handler。
避免阻塞 Actor
Handler 应保持短小。慢数据库、外部 HTTP 或长计算应拆分,避免占住 Actor 串行执行权。
尊重串行语义
Actor 不能同步 Invoke 自己;父 Actor 调子 Actor 使用 InvokeChild,避免消息重入导致死锁。
固定协议契约
Method ID 与 Protobuf 定义应版本化管理;切换默认 Codec 只在 Startup 前执行。
明确交付语义
RabbitMQ 当前为非持久、自动删除队列和尽力投递;关键业务需自行设计幂等与补偿。
建立观测基线
记录 RequestID、Method ID、ActorPath、耗时和错误码,结合 Gops 排查运行时问题。
隔离持久化细节
将 GORM、Mongo 与 Redis 封装在组件或仓储层,不让 Actor 业务直接依赖连接生命周期。
覆盖关键测试
重点验证 Method 表、Codec、上下文取消、子 Actor、连接协议和集群响应关联。
game/
├── api/ # Protobuf 契约与 Method ID
├── actor/ # 状态、Handler、事件与定时器
├── service/ # 跨 Actor 用例编排
├── repository/ # 数据访问接口与实现
├── component/ # 外部资源与生命周期适配
├── profile/ # 节点与环境配置
└── cmd/ # 各节点启动入口按需组合基础设施
组件通过 Application 生命周期统一注入。只注册节点真正需要的能力,并通过接口或仓储层隔离业务代码与基础设施实现。
Connector · Parser · HTTP Actor · Gin
GORM · MongoDB · Redis · Data Config · Distributed Lock
Cron · Gops · Logger · Profile · Time Wheel · Snowflake
提交前执行核心验证
优先覆盖协议编解码、Method Table、连接边界、HTTP 分发和 Actor 上下文生命周期;集群项目还应补充真实双节点与压力测试。
go test ./net/proto ./net/serializer ./net/method \
./net/parser ./net/httpactor ./net/actor ./net/connector下一步