Skip to content

版本迁移指南 ​

从旧版本升级 Cherry 时需要注意的破坏性变更。按影响的严重程度排序,第一条是绝大多数老项目升级时卡住的地方。

先确认你的起点

查一下当前用的版本:

bash
grep "cherry-game/cherry " go.mod
  • v1.2.x 及更早 —— 入口写法完全不同,见下面第一节
  • v1.3.x ~ v1.4.x —— 主要改组件 import 路径和 API 改名
  • v1.5.x ~ v1.6.x —— 主要是接口扩展与协议细节

一、入口从全局函数改为 Builder(v1.3.0) ​

这是最大的一次改动。 v1.2.x 用的是包级全局函数,v1.3.0 起改成 builder 模式。

旧写法(v1.2.x) ​

go
func main() {
	// 三个参数:profile 目录、profile 名、节点 id
	app := cherry.Configure("./config", "profile-dev", "game-1")

	// 包级函数逐个设置
	cherry.SetSerializer(cserializer.NewJSON())
	cherry.SetHeartbeat(30 * time.Second)
	cherry.SetDictionary(dict)
	cherry.SetDataCompression(true)

	// 包级函数注册
	cherry.RegisterComponent(myComponent)
	cherry.RegisterHandler(handler)
	cherry.RegisterConnector(conn)
	cherry.AddNodeRouter("game", routingFunc)

	// 单独启动
	cherry.Run(true, cherry.Cluster)
}

新写法(v1.3.0+) ​

go
func main() {
	// 四个参数:配置路径、节点 id、是否前端节点、节点模式
	app := cherry.Configure("./config/profile.json", "game-1", true, cherry.Cluster)

	app.Register(myComponent)
	app.AddActors(&ActorGame{})
	// 解析器、序列化器、集群都挂在 builder 上
	app.SetNetParser(agentActor)

	app.Startup()
}

对照表 ​

v1.2.xv1.3.0+说明
Configure(dir, name, nodeID)Configure(filePath, nodeID, isFrontend, mode)返回 *AppBuilder 而非 IApplication
Run(isFrontend, mode)app.Startup()启动方式移入 builder
RegisterComponent(c)app.Register(c)
RegisterHandler(h)由 Actor 的 Local() / Remote() 注册替代处理函数模型整体换成了 Actor
RegisterConnector(conn)parser.AddConnector(conn)连接器改挂在网络解析器上
RegisterCommand(cmd)—命令机制已移除
AddNodeRouter(type, fn)Actor 路径 + SetOnDataRoute路由模型重写
SetSerializer(s)app.SetSerializer(s)v1.5.0 起 builder 才提供
SetHeartbeat(d)parser.SetHeartbeat(d)
SetDictionary(d)parser.SetDictionary(d)
SetDataCompression(b)parser.SetDataCompression(b)
cherry.App()通过 app 变量或组件内的 App()不再有包级全局 Application
PostEvent(e)app.ActorSystem().PostEvent(data)

不要照抄网上的老教程

cherry.Configure 的三参数写法、cherry.Run(...)、cherry.RegisterComponent(...) 这些都不存在了。看到这类代码说明它针对的是 v1.2.x 或更早。

处理函数模型的改变 ​

v1.2.x 用「注册 handler」,v1.3.0 起统一为 Actor:

go
// 旧:注册一个 handler 处理某类消息
type MyHandler struct{}
func (h *MyHandler) Handle(ctx context.Context, session *csession.Session, msg *cmsg.Message) { }

// 新:写一个 Actor,在 OnInit 里注册函数
type MyActor struct{ cactor.Base }
func (p *MyActor) AliasID() string { return "my" }
func (p *MyActor) OnInit() {
	p.Local().Register("handle", p.handle)
}

这是迁移工作量最大的部分,原 handler 的业务逻辑要拆成 Actor 的注册函数。

二、组件迁出独立仓库(v1.4.0) ​

组件从 cherry 主仓库迁到了 cherry-game/components,模块路径变了。

旧路径新路径
github.com/cherry-game/cherry/components/crongithub.com/cherry-game/components/cron
github.com/cherry-game/cherry/components/data-configgithub.com/cherry-game/components/data-config
github.com/cherry-game/cherry/components/etcdgithub.com/cherry-game/components/etcd
github.com/cherry-game/cherry/components/gingithub.com/cherry-game/components/gin
github.com/cherry-game/cherry/components/gopsgithub.com/cherry-game/components/gops
github.com/cherry-game/cherry/components/gormgithub.com/cherry-game/components/gorm
github.com/cherry-game/cherry/components/mongogithub.com/cherry-game/components/mongo

规律很简单:去掉中间那段 cherry/。

bash
# 批量替换(Mac/Linux)
grep -rl "cherry-game/cherry/components/" --include="*.go" . \
  | xargs sed -i '' 's|cherry-game/cherry/components/|cherry-game/components/|g'

# 然后重新拉依赖
go mod tidy

组件版本独立了

迁出后组件的版本号与框架解耦。当前框架是 v1.6.8,组件是 v1.5.0。升级框架不一定要跟着升组件。

三、Go 版本要求提高 ​

框架版本要求
v1.3.x ~ v1.4.11Go 1.18
v1.4.12 起Go 1.23
v1.5.0 起Go 1.24

etcd 组件有额外限制

components/etcd 的 go.mod 锁定 cherry v1.5.0,它调用的 ComponentDefault.InitFields() 在 v1.6.0 起已被移除。

所以用 etcd 做发现服务的节点目前只能停在框架 v1.5.0,除非改用 nats 发现模式。详见 etcd 组件。

四、API 改名与删除 ​

变更生效版本说明
discovery.RegisterDiscovery() → Register()v1.3.0
SessionEqual() 移除v1.4.7用 session 自身字段比较
BroadcastSession() 移除v1.4.7改用 PushWithUIDS 按 UID 推送
PushUIDS() → PushWithUIDS()v1.4.8纯改名
RawReply() → RequestReply()v1.6.3同时 RawRequest() 改为走普通 NATS Request
Id → ID 命名统一—如 NodeId() → NodeID()
protobuf import 换成 google.golang.org/protobuf/proto—从旧的 github.com/golang/protobuf 迁移

别被老文档里的 API 误导

网上一些老教程和旧 README 里出现的 p.EventRegister(...) 从来不是真实 API——p.Event() 这个门面从 Actor 模型引入起就存在,代码里从未有过 EventRegister。类似的还有 capp.ActorLogger、两参数写法的 NewTimeWheel(tick, hint),都是文档写错了而不是版本变化。

判断一个 API 是否真的存在过,用 git grep 在自己的版本上查一下最可靠。

RawReply 只在 v1.6.2 短暂存在

RawReply 这个名字只在 v1.6.2 这一个版本里出现过,v1.6.3 就改成了 RequestReply。如果你正好用的 v1.6.2,升级时会遇到这个改名。

五、协议层细节(v1.6.8) ​

pomelo 解析器的路由辅助函数移除了 session 参数:

go
// v1.6.7 及更早
func DefaultDataRoute(agent *Agent, route *Route, msg *Message, session *cproto.Session)
func LocalDataRoute(agent *Agent, route *Route, msg *Message, session *cproto.Session, targetPath string)

// v1.6.8 起
func DefaultDataRoute(agent *Agent, route *Route, msg *Message)
func LocalDataRoute(agent *Agent, route *Route, msg *Message, targetPath string)

session 改由函数内部按 agent.NewSession(msg.ID) 构造。

只有 pomelo 改了

simple 解析器的签名没跟着改,它的 LocalDataRoute 仍然接收 session,而 DefaultDataRoute 的参数顺序也与 pomelo 相反。两个协议不要一起改。详见 网络数据包。

同属 v1.6.8 的相关变更:Session 结构拆出了带读写锁的 SessionData,SetMID / GetMID 从 map 改成了 proto 字段。这些是框架内部调整,业务代码一般不直接受影响。

六、接口扩展(非破坏性) ​

这些是新增方法,老代码不受影响,但自定义实现需要注意:

版本新增
v1.5.0ICluster.Mode()、PublishRemoteType();AppBuilder 补上 SetSerializer / SetDiscovery / SetCluster
v1.6.1ITimer.New() / NewOnce()、ITimerHandle.SetNext()
v1.6.3ICluster.RequestReply()、RawPublish()、RawRequest()
v1.6.4ICluster.RequestSync()
v1.6.xIDiscovery.UpdateSetting() / UpdateSettings()

自定义实现要补方法

如果你自己实现了 IClusterComponent 或 IDiscoveryComponent,框架加了接口方法后你的实现会编译不过。按报错补上对应方法即可。

升级步骤建议 ​

按这个顺序做,每一步都能编译通过:

  1. 先升 Go 版本到 1.24,避免后面被工具链问题干扰
  2. 替换组件 import 路径(第二节),go mod tidy
  3. 改用 Builder 入口(第一节),这一步工作量最大
  4. 处理 API 改名(第四节),编译器会把用错的地方都指出来
  5. 对齐协议层签名(第五节),只在自定义了路由函数时才需要
  6. 跑一遍示例验证:先跑 demo_chat(不依赖外部服务),再跑 demo_gorm,最后跑 demo_cluster

编译器是你的朋友

除了「入口写法改了」这一类,其余大部分变更(改名、删函数、接口扩展)都会直接编译报错。先让代码编过,再处理运行时行为差异。

相关 ​