3 个环节打通 ET 框架 Actor 模型跨进程通信:分布式消息完整指南

发布时间:2026/9/17 10:09:26

3 个环节打通 ET 框架 Actor 模型跨进程通信:分布式消息完整指南 3 个环节打通 ET 框架 Actor 模型跨进程通信分布式消息完整指南【免费下载链接】ETUnity3D Client And C# Server Framework项目地址: https://gitcode.com/GitHub_Trending/et/ET凌晨 3 点玩家从 1 线跨线进 2 线。传送刚完成Map 进程里一条Frame_ClickMap还按旧地址投给了 1 线——那个 Unit 已经不存在了消息石沉大海玩家原地卡住不动。这不是网络抖动而是分布式服务端的典型地址失效问题也是 ET 框架Unity3D 客户端 C# 服务器单线程多进程用 Actor 模型要正面解决的事让给对象发消息这件事不依赖你记住它在哪个进程。![ET 框架 Actor 模型游戏场景示例](https://raw.gitcode.com/GitHub_Trending/et/ET/raw/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.lockstep/Assets/GameRes/Loading/Sprites/Warrior_Background2 1.png?utm_sourcegitcode_repo_files)选型篇ET 站在 Erlang 和 Skynet 中间什么位置ET 选了单线程多进程每个进程内只跑一个逻辑线程多核靠多进程吃满。这和 Erlang、Skynet 的单进程多线程正好相反也决定了三者 Actor 的最小粒度完全不同。维度ETErlangSkynet运行形态单线程多进程单进程多线程单进程多线程并发单元Entity 对象Erlang 进程Lua 服务节点单元寻址Entity.InstanceId高 16 位编码进程号Pid 进程号服务地址单元内调度Fiber 协程调度器线程池服务内单线程多机部署天然同构单机集群雏形需分布式 Erlang需额外方案关键差异在第二行Erlang 和 Skynet 的消息只能发到进程/服务这一级发给谁得消息里再带个 id 二次分发ET 把 Actor 下沉到 Entity 对象级一个 Unit、一个 GateSession 本身就是收件人。换句话说把 ET 的整个进程Game.Scene当成一个 Actor就退化成了 Erlang 的进程级模型——Erlang 是 ET 的特例而不是反过来。依据见 Actor Model 官方说明。落地篇用一条 Unit 点击消息走通全链路下面用一个贯穿例子Map 进程里的 Unit 要给自己挂的 GateSession 发一条消息让客户端表现移动全程只用InstanceId寻址。发送从 InstanceId 拿到收件通道// 按 InstanceId 拿到目标 Actor 的 sender var sender Game.Scene.GetComponentActorSenderComponent() .Get(unitGateComponent.GateSessionActorId); sender.Send(message); // 单向投递 var response sender.Call(message); // RPC拿到回包发送方不需要知道 GateSession 在哪个进程。InstanceId的高 16 位直接编码了进程号框架据此决定本地入队还是走跨进程网络这一步对业务代码完全透明。路由这个 InstanceId 是怎么传到 Map 手里的它不是凭空产生的是登录链路上带过来的// Gate 建立会话时挂邮箱标记该 Entity 是 Actor session.AddComponentMailBoxComponent, string(MailboxType.GateSession); // 玩家进地图时把 GateSession 的 InstanceId 传给 Map M2G_CreateUnit createUnit (M2G_CreateUnit)await mapSession.Call( new G2M_CreateUnit() { PlayerId player.Id, GateSessionId session.InstanceId });MailBoxComponent是该 Entity 是 Actor的标记也是它的收件箱。邮箱分两种类型GateSession型收到消息立刻转发给客户端MessageDispatcher型默认把消息按类型分发给对应的 Handler。同一份基础设施两条投递路径。处理两种 Handler 的写法MessageDispatcher邮箱分发时Send 消息和 RPC 消息分别对应两个抽象基类// Send继承 AMActorHandlerActor类型, 消息类型 [ActorMessageHandler(AppType.Map)] public class Actor_TestHandler : AMActorHandlerUnit, Actor_Test { protected override ETTask Run(Unit unit, Actor_Test message) { Log.Debug(message.Info); return ETTask.CompletedTask; } }RPC 多一个响应泛型并通过reply回调回包// RPC继承 AMActorRpcHandlerActor类型, 请求, 响应 [ActorMessageHandler(AppType.Map)] public class Actor_TransferHandler : AMActorRpcHandlerUnit, Actor_TransferRequest, Actor_TransferResponse { protected override async ETTask Run(Unit unit, Actor_TransferRequest message, ActionActor_TransferResponse reply) { try { reply(new Actor_TransferResponse()); } catch (Exception e) { ReplyError(e, reply); } await ETTask.CompletedTask; } }到这里知道 InstanceId → 投递 → 排队 → Handler的完整链路就跑通了。排坑篇三个线上高频坑⚠️ 坑一Handler 里互相 Call整条消息队列冻住现象ACallBBCallCC 又CallA之后三个 Entity 的所有消息再无人处理。根因MailBoxComponent本质是串行队列一条 Handler 没返回ETTask后续消息全部排队等待Call要阻塞等回包环上任何一环等待就全环死锁。解法让 Handler 快速返回把等待挪到独立协程protected override ETTask Run(Unit unit, Actor_Test message) { HandleAsync(unit, message).Coroutine(); // 开新协程不占队列 return ETTask.CompletedTask; }⚠️ 坑二跨服传送窗口期RPC 报Actor 不存在现象玩家传送期间发给他的 RPC 持续失败客户端表现为点了没反应。根因传送时旧进程先删 Entity新进程还没建好这段时间按缓存的InstanceId投递必然落空。解法改用带 Location 的 sender代码见进阶篇它内置失败 → 重查地址 → 重投窗口期消息被锁等待自动吸收业务代码不用改。⚠️ 坑三每条消息都查一次位置服务现象高频给同一对象发消息时Location Server 查询量暴增序列化开销吃掉收益。根因对象迁移是低频事件但查询如果每次都发就按消息频率放大了跨进程 RPC。解法本地缓存InstanceId。ActorLocationSender首条消息才查 Location Server之后直接复用缓存投递失败再回查并重试默认 5 次。配套手段高频小消息合并成一条再发非实时数据攒批处理降低队列与序列化压力。进阶篇Actor Location——换工位不丢消息InstanceId 会随进程迁移而变但Entity.Id终身不变。Actor Location 机制就是让不变的 Id可以寻址会变的 InstanceId。用企业比喻最直观Entity.Id是工号入职终身不变InstanceId是工位号换办公室进程就得换号。Location Server 是前台总机存着工号 → 当前工位号的登记表。Actor 在任何进程创建或迁移完成时都要去总机刷新登记。只报工位号直接投只报工号先查总机再投。投递途中对方刚搬走会收到工位已空发件方等 1 秒、重查总机、重投最多重试 5 次仍失败则抛异常。搬家期间该工号在总机上加锁查这个工号的请求全部排队等搬家完成防止消息投在半路状态里。发送与处理代码与基础 Actor 几乎一致只是基类多了 Location 字样// 按 Entity.Id 拿 Location sender var locationSender Game.Scene .GetComponentActorLocationSenderComponent().Get(unitId); locationSender.Send(message); var response await locationSender.Call(request);[ActorMessageHandler(AppType.Map)] public class Frame_ClickMapHandler : AMActorLocationHandlerUnit, Frame_ClickMap { protected override ETTask Run(Unit unit, Frame_ClickMap message) { Vector3 target new Vector3(message.X, message.Y, message.Z); unit.GetComponentUnitPathComponent().MoveTo(target).Coroutine(); return ETTask.CompletedTask; } }另有一句新人最容易误解的话要讲清Actor 是纯服务端通信机制。客户端消息看起来像 Actor 消息只是 Gate 复用了服务端 Actor 通道做转发并非客户端也跑了一套 Actor。详见 Actor Location 说明。收尾篇上线前检查清单✅ 跨进程传递的位置信息一律用Entity.IdInstanceId只做进程生命周期内的短途缓存✅ 所有可能等待回包的 Handler确认已用RunAsync(...).Coroutine()脱离消息队列✅ 需要跨线/跨场景寻址的对象统一走ActorLocationSender不裸发 InstanceId✅ 高频小消息已合并非实时数据已攒批Location 重试 5 次失败后的异常有日志与兜底学习资源Actor Model 官方说明Actor Location 中文说明项目 README【免费下载链接】ETUnity3D Client And C# Server Framework项目地址: https://gitcode.com/GitHub_Trending/et/ET创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/17 10:04:26

汽车电子PCBA应力测试实战:焊点暗裂预防与关键点位全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/17 11:14:41

Win11下JDK1.8与JDK17双环境管理实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/17 11:14:41

AI/Agent大规模部署为何首选PolarDB?六大能力深度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/17 11:14:41

OpenClaw开源AI工具链:GPT-5.4适配与记忆热插拔技术解析

1. OpenClaw 项目概览:当开源工具链遇上AI新范式OpenClaw作为开源AI工具链的标杆项目,近期迎来里程碑式更新。这个最初由开发者社区孵化的项目,如今已成长为支持多模态AI开发的完整生态平台。最新版本的核心升级点在于对GPT-5.4架构的深度适配…

2026/9/17 11:14:41

OpenMontage:面向AI智能体的声明式任务编排引擎

1. 项目概述:OpenMontage 不是视频剪辑软件,而是一套面向 AI 原生工作流的“智能编排引擎”OpenMontage 这个名字一出来,很多人第一反应是“哦,又一个开源视频编辑工具”,毕竟 montage 在影视行业里就是“剪辑、拼接”…

2026/9/17 11:14:41

公共管理大数据实战:数据治理、预测分析与可视化

简介:这份资料以《大数据在公共管理中的应用》为主题的PPT演示文稿,面向公共管理、行政管理及相关专业的师生与政务信息化从业者,适合课堂汇报、专题培训与政策研究等场景使用。内容从大数据的概念与来源切入,梳理海量性、高速性、…

2026/9/17 11:09:40

PeopleSoft Application Engine 批处理执行与重启排错

简介:面向PeopleSoft开发与运维人员的中文技术文档,围绕人力系统中的Application Engine批量处理、定时任务与模块集成展开。内容基于Enterprise PeopleTools 8.50 PeopleBook整理翻译,涵盖应用引擎概述、实施步骤、程序元素与Meta-SQL&#…

2026/9/16 12:52:37

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/17 0:03:13

WiFi密码安全测试:从原理到实战的字典暴力破解指南

1. 写在前面:我为什么要研究WiFi密码这件事先交代一下背景。我身边有不少朋友,家里的WiFi密码常年是"12345678"或者"88888888",问就是"好记"。直到有一次,隔壁邻居蹭网蹭到我家路由器后台都进不去&…

2026/9/17 0:03:13

redis-py服务控制与监控函数实战:从ping到slowlog的巡检指南

我用 redis-py 写了快五年的业务代码,坦白说,真正让我觉得这个客户端“像一个成熟工具箱”的,不是 get/set 那套基本操作,而是它那批专门做服务控制与状态监控的辅助函数。日常开发里,大家把redis.Redis(host..., deco…

2026/9/17 0:03:13

SpringBoot+Vue3实现中小企业设备管理系统开发实践

1. 项目概述与核心价值中小企业设备管理系统是制造业、服务业等领域的基础信息化工具。传统设备管理往往依赖Excel表格或纸质记录,存在数据孤岛、流程混乱、维护成本高等痛点。这套基于Java SpringBootVue3MyBatis的技术方案,通过前后端分离架构实现了设…

2026/9/16 22:55:57

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/16 22:56:09

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/16 22:56:16

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码