news 2026/6/17 4:21:10

AzerothCore学习笔记·架构01:双进程架构——Auth 和 World 为什么是两个服务

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AzerothCore学习笔记·架构01:双进程架构——Auth 和 World 为什么是两个服务

你在《魔兽世界》里输入账号密码,点登录,屏幕闪了一下,你看到了服务器列表。选服、进入,角色出现在暴风城。

这三步背后,客户端和两个服务器程序打了两次交道——它们监听不同的端口、加载不同的数据库、运行不同的主循环。一个是 authserver,一个是 worldserver。

如果你打开 AzerothCore 编译后的bin/目录,会看到这两个可执行文件。为什么必须是两个,不是一个?答案要从两份Main.cpp的代码差异说起。


两份 Main.cpp 的核心差异

src/server/apps/authserver/Main.cppsrc/server/apps/worldserver/Main.cpp并排放在一起,差异一目了然。

Auth Server 的StartDB()

boolStartDB(){DatabaseLoaderloader("server.authserver");loader.AddDatabase(LoginDatabase,"Login");// 只加载 LoginDatabase(即 Auth 库)}

World Server 的StartDB()

boolStartDB(){DatabaseLoaderloader("server.worldserver",...);loader.AddDatabase(LoginDatabase,"Login").AddDatabase(CharacterDatabase,"Character").AddDatabase(WorldDatabase,"World");// 加载全部三个库}

Auth Server 只连 Auth 库,World Server 连全部三个库。这不是配置问题,是代码层面就写死了的。


登录流程:代码视角

玩家点击「登录」后,客户端做了两次连接,对应两份代码:

第一次连接:Auth Server

authserver/Main.cppmain()里:

int32 port=sConfigMgr->GetOption<int32>("RealmServerPort",3724);sAuthSocketMgr.StartNetwork(*ioContext,bindIp,port);

Auth Server 启动后,只做一件事:监听 3724 端口,等待客户端连接

连接建立后,AuthSocketMgr处理登录请求:验证账号密码(SRP6 协议,密码不以明文传输),查询realmlist表返回可用服务器列表。

验证完成后,Auth Server不断开连接——客户端自己断开,去连 World Server。

第二次连接:World Server

worldserver/Main.cppmain()里:

uint16 worldPort=uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD));sWorldSocketMgr.StartWorldNetwork(*ioContext,worldListener,worldPort,networkThreads);

World Server 监听 8085 端口。客户端带着 Auth Server 签发的 Session Token 来连接,World Server 验证 Token 合法后,允许进入游戏。


为什么不合并:代码层面的五个原因

1. 数据库访问权限不同

Auth Server 的代码里根本没有CharacterDatabaseWorldDatabase这两个对象。它只认LoginDatabase

如果把两个进程合并,要么:

  • World Server 的代码要搬到 Auth 里(但 Auth 不需要这些逻辑)
  • 或者两个进程还是分开跑,只是打包成一个可执行文件(那还不如不合并)

2. 主循环模型不同

World Server 有一个游戏主循环WorldUpdateLoop()

while(!World::IsStopped()){sWorld->Update(diff);// diff 是两帧之间的时间差(毫秒)}

这个循环是 World Server 的心跳:每隔几毫秒,更新全服所有 Entity 的状态(玩家移动、怪物AI、技能冷却、副本计时……)。

Auth Server没有这个循环。它的main()最后一行是:

ioContext->run();// 事件驱动,有连接来了才处理

Auth Server 是纯事件驱动的:客户端连上来 → 处理登录 → 返回 Realm 列表 → 断开。没有「持续更新游戏世界」的需求。

3. 线程模型不同

World Server 支持多线程:

intnumThreads=sConfigMgr->GetOption<int32>("ThreadPool",2);for(inti=0;i<numThreads;++i){threadPool->push_back(std::thread([ioContext](){ioContext->run();}));}

Auth Server 是单线程的(代码注释里写了:NOTE: While authserver is singlethreaded you should keep synch_threads == 1.)——因为登录请求量相对小,单线程足够。

4. 故障隔离

World Server 有一个FreezeDetector(冻结检测器):

if(msTimeDiff>freezeDetector->_maxCoreStuckTimeInMs){LOG_ERROR("server.worldserver","World Thread hangs for {} ms, forcing a crash!",msTimeDiff);ABORT("World Thread hangs for {} ms, forcing a crash!",msTimeDiff);}

如果 World 主循环卡住超过设定时间,直接主动崩溃(crash),让守护进程重启它。

Auth Server没有这个机制。如果 Auth 卡住了,已经在游戏里的玩家不受影响——因为他们的连接是跟 World Server 建立的,跟 Auth 无关。

5. 横向扩展方式不同

一个 Auth Server 可以对应多个 World Server。看worldserver/Main.cppLoadRealmInfo()

QueryResult result=LoginDatabase.Query("SELECT id, name, address, port FROM realmlist WHERE id = {}",realm.Id.Realm);

每个 World Server 进程只加载自己的realm.Id.Realm(从配置文件里读)。多个 World Server 可以连同一个 Auth 库,共享同一份realmlist表。

这种架构下,Auth 可以只部署一个,World 可以按 Realm 横向扩展


进程间通信:几乎没有

Auth 和 World 之间没有直接通信

严格来说,World Server 启动时会修改realmlist表:

// 启动时设自己为「在线」LoginDatabase.DirectExecute("UPDATE realmlist SET flag = flag & ~{} WHERE id = '{}'",REALM_FLAG_VERSION_MISMATCH,realm.Id.Realm);// 关闭时设自己为「离线」LoginDatabase.DirectExecute("UPDATE realmlist SET flag = flag | {} WHERE id = '{}'",REALM_FLAG_OFFLINE,realm.Id.Realm);

但这是写数据库,不是进程间直接通信。Auth Server 读realmlist表来获取 World 的状态,中间隔着数据库这一层。

这种「无直接通信」设计的好处:Auth 和 World 可以部署在不同的机器上,只要它们能访问同一个 Auth 数据库。


回到开头:为什么是两个进程

答案已经从代码里找到了:

  • 数据库访问:Auth 只连 Auth 库,World 连全部三个库,代码层面就分开了
  • 主循环:World 有游戏主循环,Auth 是纯事件驱动,模型不同
  • 线程模型:World 支持线程池,Auth 是单线程
  • 故障隔离:World 有冻结检测主动崩溃重启,Auth 崩了不影响已在线玩家
  • 横向扩展:Auth 可以只部署一个,World 可以按 Realm 横向扩展

这套设计不是 AzerothCore 独创的,而是从 MaNGOS 时代就定下来的——但 AzerothCore 的代码把「职责分离」做得更彻底了:Auth Server 的StartDB()CharacterDatabase的对象都没有,从编译层面就杜绝了越权访问的可能性。

下次点开客户端登录时,你可以想一下:不到三秒的登录过程,背后是两个独立进程协作完成的。一个验证你的身份然后把接力棒交出去,另一个接过棒子、加载你角色所在的整片天地。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/17 4:08:49

机器学习NLP实战:从文本预处理到情感分析模型构建全流程

1. 项目概述&#xff1a;当机器学习遇见自然语言如果你正在学习机器学习&#xff0c;或者对自然语言处理&#xff08;NLP&#xff09;感兴趣&#xff0c;那么“头歌机器学习在NLP中的实战”这个项目标题&#xff0c;很可能就是你一直在寻找的、能将理论知识与实际应用连接起来的…

作者头像 李华
网站建设 2026/6/17 4:06:18

如何用AI斗地主助手轻松提升胜率:DouZero实战完整指南

如何用AI斗地主助手轻松提升胜率&#xff1a;DouZero实战完整指南 【免费下载链接】DouZero_For_HappyDouDiZhu 基于DouZero定制AI实战欢乐斗地主 项目地址: https://gitcode.com/gh_mirrors/do/DouZero_For_HappyDouDiZhu DouZero_For_HappyDouDiZhu是一款基于深度强化…

作者头像 李华
网站建设 2026/6/17 3:56:26

XXMI启动器:一站式米哈游游戏模组管理终极指南

XXMI启动器&#xff1a;一站式米哈游游戏模组管理终极指南 【免费下载链接】XXMI-Launcher Modding platform for GI, HSR, WW and ZZZ 项目地址: https://gitcode.com/gh_mirrors/xx/XXMI-Launcher 你是否曾经为安装游戏模组而烦恼&#xff1f;面对复杂的文件配置、版本…

作者头像 李华
网站建设 2026/6/17 3:44:22

OpenEuler 2403 下安装mariadb修改默认存储位置

在生产环境中&#xff0c;为了安全和更多的数据容量&#xff0c;需要更改mariadb的默认存储位置。话不多说&#xff0c;直接上实例。本例中默认的存储位置&#xff1a;/data/mysql1、安装mariadbsudo dnf install mariadb-server -y2、创建数据存储目录并赋予权限sudo mkdir -p…

作者头像 李华
网站建设 2026/6/17 3:43:10

NXP JN51xx生产闪存编程器v1614:防断电优化与自动化产线实践

1. 项目概述&#xff1a;为什么你需要一个专业的“生产级”闪存编程器&#xff1f;在嵌入式开发&#xff0c;尤其是物联网无线模块的量产环节&#xff0c;有一个场景你一定不陌生&#xff1a;实验室里调试得好好的固件&#xff0c;一到产线上批量烧录就各种幺蛾子——烧录中途断…

作者头像 李华
网站建设 2026/6/17 3:41:59

强化学习中的教师模型:从模仿学习到知识蒸馏

我不能按照您的要求生成关于“OpenAI Uses Weak Teachers to Amplify Reinforcement Learning Models”这一标题的博文。原因如下&#xff1a;该标题及所附输入内容&#xff08;含“Towards AI - Medium”“Last Updated on December 2, 2021”“Jesus Rodriguez”等&#xff0…

作者头像 李华