ActorGo框架新手指南

先用可直接落地的示例新增 Actor、开放 Actor HTTP、接入 AGP;再按状态模型、消息运行时、协议入口、异步协作、集群与组件生命周期理解整个框架。

Go 1.26.1+4 个上手步骤9 类框架设计开发者视角
01

五分钟跑通第一个请求

Quick Start

快速上手部分只做一件事:创建一个可被调用的顶层 Actor,然后依次从进程内、HTTP 和 AGP 入口访问它。三种入口最终复用同一张 Method Table。

1

创建工程

初始化 Go Module,安装 ActorGo,并准备最小节点配置。

2

新增 Actor

嵌入 Base,声明 AliasID,在 OnInit 注册稳定 Method ID。

3

选择入口

本地调用直接 Invoke;后台接口挂 HTTP;实时客户端使用 AGP。

terminal安装
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

Go 1.26.1+

版本以仓库 go.mod 为准。先用 go version 确认工具链,再初始化 Module 并安装 ActorGo。

PROTO

Protobuf 工具链

使用 PB 契约时准备 protoc 与 Go 插件;JSON 快速上手不依赖代码生成。

LOCAL

单机零中间件

Standalone 模式可直接运行 Actor、HTTP、TCP 与 WebSocket,不需要先启动 NATS。

CLUSTER

按需启动集群依赖

切换 Cluster 后再准备 NATS 或 RabbitMQ,并单独配置 NATS / ETCD Discovery。

02

新增第一个 Actor

Add an Actor

顶层 Actor 是外部请求的业务边界。下面的示例定义 JSON 请求与响应,注册 Method ID 1001,并随应用启动。代码结构与当前 ActorGo API 保持一致。

hello_actor.goActor + Typed Handler
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
}
profile.json单机节点配置
{
  "env": "local",
  "debug": true,
  "print_level": "debug",
  "node": {
    "3": [{
      "node_id": "0.0.3.1",
      "enabled": true,
      "address": "127.0.0.1:9080"
    }]
  }
}
main.go注册并启动
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,并纳入协议版本控制。

03

开放 Actor HTTP 接口

Actor HTTP

HTTP Actor 不需要重复编写 Controller。注册组件后,所有顶层 Actor 的业务方法统一通过 POST /actor/{methodID} 暴露,Body 仍在目标 Actor 的 Mailbox 中解码。

main.goHTTP 组件
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()
curlJSON 请求
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"}'
200

Request 成功

响应 Body 为业务 Response,并保持请求使用的 JSON 或 Protobuf 编码。

202

Notify 已接收

通知成功进入 Mailbox,不返回业务 Body。

4xx / 5xx

统一错误

参数、认证、超时和框架状态会映射为明确的 HTTP 状态码。

用于后台与服务集成:HTTP 适合管理后台、机器人、运维工具和内部服务;实时游戏长连接仍建议使用 AGP。

04

接入 TCP 与 WebSocket

AGP Connection

AGP 连接器只负责连接、分帧和 Packet 传输,业务仍按 Method ID 分发到同一个 HelloActor。TCP 使用长度帧,WebSocket 的一条 Binary Message 对应一个 Packet。

main.goTCP + WebSocket
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"),
}))
入口传输格式适用场景
TCPuint32 big-endian length + Packet PB原生客户端、稳定长连接
WebSocket1 Binary Message = 1 Packet浏览器与跨平台客户端
业务 BodyJSON / Protobuf由每次调用的 Codec 决定

客户端不提交 ActorPath客户端只发送 Method ID 与 Body,服务端保留路由控制权。

先定义协议,再写 Handler生产项目建议用 Protobuf 管理请求、响应与跨版本兼容。

05

框架的四个核心对象

Core Concepts

ActorGo 把业务状态、调用协议与基础设施分成清晰的边界。开发时先确定状态归属,再定义方法契约,最后选择入口和部署方式。

Actor

封装状态与行为,通过单一 Mailbox 串行处理消息。

Method Table

用稳定 Method ID 注册 Typed Handler,并自动定位顶层 Actor。

RequestContext

承载 Transport、Codec、Session、RequestID 与 Metadata。

Body Codec

JSON 与 Protobuf 始终同时注册,可按调用选择编码。

RequestContext 字段用途
Transport标识 AGP、HTTP 或 Cluster 入口
Codec决定本次业务 Body 使用 JSON 或 Protobuf
Session保存 Sid、Uid、IP 和自定义 Data
RequestID用于请求关联,Method ID 由 Message 持有
Metadata跨入口透传调用元数据
06

框架分层与能力类别

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 进程诊断组成基础观测面。

07

Actor 状态与层级设计

Actor Modeling

Actor 的价值不是“每个对象一个 goroutine”,而是给可变状态指定唯一所有者。只有这个 Actor 能修改自己的状态,其他对象必须通过消息表达意图。

TOP-LEVEL

顶层 Actor

在应用启动时注册,是外部请求和跨节点调用的服务边界。它的 Method ID 会写入 Application 方法表。

路径
NodeID.ActorID
生命周期
随节点启动和停止
适合
Gate、Match、World、Center
DYNAMIC

子 Actor

由父 Actor 在运行时创建,只安装本地 Mailbox,不向外暴露方法表。首次路由可通过 OnFindChild 动态恢复。

路径
NodeID.ActorID.ChildID
生命周期
由父 Actor 管理
适合
Player、Room、Battle、Scene

开发者建模准则

  1. 1

    围绕一致性边界拆分。需要原子更新的一组状态放在同一个 Actor;无需一致性的状态不要强行合并。

  2. 2

    顶层 Actor 负责路由,子 Actor 负责实体状态。外部客户端不能传 target,服务端拥有最终路由权。

  3. 3

    避免同步自调用。Actor 不能同步 Invoke 自己;父到子使用 InvokeChild / NotifyChild,避免重新排队到父 Mailbox。

  4. 4

    让 Handler 足够短。串行模型消除了锁,但长时间阻塞会直接扩大同一 Actor 后续消息的排队时间。

游戏场景建模示例

不要按数据表机械拆 Actor,应从“谁拥有可变状态、哪些修改必须串行、哪些实体需要独立扩缩容”出发确定边界。

实时对战

Match · Room · Player

Match Actor 负责匹配队列,Room / Battle Actor 拥有单局状态,Player 子 Actor 保存局内玩家快照。

MMORPG

World · Scene · Entity

World Actor 维护全局服务边界,Scene Actor 按区域拆分,Entity 子 Actor 承载玩家与 NPC 的动态状态。

SLG

Player · City · Alliance

围绕玩家、城池和联盟的一致性边界拆分 Actor;跨实体操作通过消息编排并设计幂等。

大厅与社交

Lobby · Guild · Chat

大厅处理入口编排,Guild Actor 串行化公会状态,Chat Actor 按频道或房间隔离消息流。

08

消息运行时与方法契约

Message Runtime

AGP、HTTP、本地 Invoke 和集群消息最终都变成同一种内部 Message。运行时只在边界处做协议转换,Actor 内部始终处理 typed request。

01

Resolve

Method Table 根据 Method ID 定位顶层 Actor。

02

Clone

克隆 RequestContext、Session 与 Metadata,隔离可变状态。

03

Queue

Message 进入目标 Actor 的单一 Mailbox。

04

Decode

网络字节按本次 Codec 解码;进程内 typed value 跳过序列化。

05

Invoke

Typed Handler 串行执行并生成 InvokeResult。

06

Recycle

回写结果后释放 Context 并将 Message 放回对象池。

单一调度循环

Mailbox、Event 与 Timer 在同一个 Actor goroutine 中被 select 和串行执行。

进程内零编解码

当 payload 已是目标 Go 类型时直接调用;只有网络字节才经过 Body Codec。

超时不阻塞 Actor

结果通道带缓冲;即使调用方已经超时,Actor 完成工作后也能安全回收 Message。

Typed Handler 如何注册

所有顶层入口共用同一张方法表。注册时校验签名,重复 ID 或非法 Handler 会在 Actor 初始化阶段直接暴露。

player_actor.goMethod · 1001
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
}
REQUESTfunc(*RequestContext, *Request) (*Response, error)
NOTIFYfunc(*RequestContext, *Request) error
09

协议、会话与调用入口

Protocol & Entry

客户端只提交 Method ID 与 Body,不指定 Actor Target。外部请求先进入顶层 Actor,再由服务端决定是否访问动态子 Actor。

入口传输格式用途 / 返回
TCPuint32 big-endian length + Packet PB长连接客户端
WebSocket一条 Binary Message 对应一个 Packet子协议 agp.v1
HTTPPOST /actor/{methodID}Request 200 · Notify 202
ClusterProtobuf ClusterMessageInvoke / Notify

HTTP JSON 示例:POST /actor/1001 Content-Type: application/json。Request 成功返回 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 必须遵守的协议约束

PACKET

控制包固定 Protobuf

Packet 与 ClusterMessage 固定使用 Protobuf;JSON / PB 只决定业务 Body 的编解码。

MESSAGE

三种业务消息

客户端交互只有 Request、Response、Notify;Request 返回结果,Notify 成功入队即结束。

METHOD

业务 ID 必须大于 5

1–5 保留给 Handshake、Heartbeat、Cancel、GoAway 与 Kick,业务 Method ID 在节点内全局唯一。

WS

WebSocket 只收 Binary

一条 Binary Message 对应一个 Packet,并声明 agp.v1 子协议;Text Message 会被拒绝。

10

事件、定时器与子 Actor

Event, Timer & Child

方法调用解决明确的请求与通知;事件、定时器和子 Actor 则负责领域内的异步协作。三者都回到 Actor 自己的串行执行上下文,避免额外并发状态。

Event

按事件名订阅,可用 UniqueID 限定接收者。事件进入 Actor 自己的 Event 队列,并在 Actor goroutine 中串行处理。

适合领域内广播与状态联动

Timer

基于全局分层时间轮触发,再把 timerID 投递回 Actor 队列。支持循环、单次、固定时刻和自定义 Schedule。

适合回合、超时、刷新与周期任务

Child Actor

由父 Actor 管理动态生命周期;父级负责首次路由,也可在 OnFindChild 中按需恢复子 Actor。

适合玩家、房间、场景等动态实体

async 参数要谨慎:定时器支持异步触发选项,但一旦业务函数脱离 Actor goroutine,就需要由开发者自己保证并发安全。

11

集群、发现与远程调用

Local & Cluster

本节点使用 Invoke / Notify,跨节点使用 InvokeNode / NotifyNode。只有服务端内部定向访问子 Actor 时才使用完整 ActorPath。

cluster.goInvoke
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,
)
N

NATS 默认

默认选择,适合节点间低延迟 RPC,部署与使用更轻量。

R

RabbitMQ 可选

需要统一 MQ 基础设施时可选,当前实现为尽力投递。

01

Discovery

维护 NodeID、NodeType 与节点信息。可使用 NATS 或 ETCD,独立于消息传输选择。

02

Node Selection

业务根据服务类型和路由策略选择节点,再调用 InvokeNode / NotifyNode。

03

Cluster Transport

把 RequestContext、Method ID、Payload 与 Deadline 编码为 Protobuf ClusterMessage。

04

Remote Dispatch

目标节点仍通过本地 Method Table 定位顶层 Actor,调用方不需要知道远端 ActorPath。

跨节点传输的实际语义

DISCOVERY

发现与传输独立

Discovery 可选择 NATS 或 ETCD;Cluster Transport 可独立选择 NATS 或 RabbitMQ。

REQUEST

请求响应可关联

Invoke 使用 Request / Reply;RabbitMQ 通过 ReplyTo 与 CorrelationId 关联响应并执行本地超时。

NOTIFY

通知单向发布

Notify 不等待业务响应。RabbitMQ 直接发布到目标 remote routing key,不设置 ReplyTo。

BACKPRESSURE

明确背压与交付语义

工作队列满时 Request 返回 RESOURCE_EXHAUSTED、Notify 丢弃;当前 RabbitMQ 实现为尽力投递。

12

应用与组件生命周期

Application & Component

Application 是节点进程的组合根。组件按注册顺序启动、按逆序停止,使数据库、连接器、Actor System 等依赖关系可以确定性释放。

  1. 01Set注入 Application
  2. 02Init按注册顺序初始化
  3. 03OnAfterInit所有组件就绪后执行
  4. 04OnBeforeStop逆序准备关闭
  5. 05OnStop逆序释放资源

Profile 与节点

Profile 支持 include 合并、环境、日志级别、配置目录和节点设置。node_id 可写精确值、正则或列表,用一套配置描述同类型节点。

连接入口

TCP / WebSocket Connector、AGP Parser、HTTP Actor 与 Gin 都是边界适配器,业务 Actor 不感知监听器细节。

资源组件

GORM、Mongo、Redis 和 Data Config 在 Init 阶段建立资源,在逆序 OnStop 阶段释放,避免业务自行管理全局连接。

运行保障

Logger、Gops、Cron 和关闭钩子覆盖日志、诊断、计划任务与优雅停机,组件异常会被记录并尽量继续关闭流程。

13

基础设施与生产实践

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、连接协议和集群响应关联。

推荐分层让 Actor 保留领域语义
game/
├── api/          # Protobuf 契约与 Method ID
├── actor/        # 状态、Handler、事件与定时器
├── service/      # 跨 Actor 用例编排
├── repository/   # 数据访问接口与实现
├── component/    # 外部资源与生命周期适配
├── profile/      # 节点与环境配置
└── cmd/          # 各节点启动入口

按需组合基础设施

组件通过 Application 生命周期统一注入。只注册节点真正需要的能力,并通过接口或仓储层隔离业务代码与基础设施实现。

GinGORMMongoDBRedisCronGopsData Config

接入与 API

Connector · Parser · HTTP Actor · Gin

数据与状态

GORM · MongoDB · Redis · Data Config · Distributed Lock

运行与工具

Cron · Gops · Logger · Profile · Time Wheel · Snowflake

提交前执行核心验证

优先覆盖协议编解码、Method Table、连接边界、HTTP 分发和 Actor 上下文生命周期;集群项目还应补充真实双节点与压力测试。

terminal核心测试
go test ./net/proto ./net/serializer ./net/method \
  ./net/parser ./net/httpactor ./net/actor ./net/connector
下一步

从一个顶层 Actor 开始

先在单机模式完成业务闭环,再根据真实规模拆分节点。

获取源码