惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Engineering at Meta
Engineering at Meta
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Martin Fowler
Martin Fowler
雷峰网
雷峰网
Recent Announcements
Recent Announcements
博客园 - 叶小钗
F
Full Disclosure
C
Check Point Blog
A
About on SuperTechFans
L
LangChain Blog
Vercel News
Vercel News
T
The Blog of Author Tim Ferriss
博客园 - 司徒正美
C
Cybersecurity and Infrastructure Security Agency CISA
C
CXSECURITY Database RSS Feed - CXSecurity.com
Cisco Talos Blog
Cisco Talos Blog
Hugging Face - Blog
Hugging Face - Blog
Recorded Future
Recorded Future
MongoDB | Blog
MongoDB | Blog
Project Zero
Project Zero
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Know Your Adversary
Know Your Adversary
D
Docker
U
Unit 42
酷 壳 – CoolShell
酷 壳 – CoolShell
T
The Exploit Database - CXSecurity.com
L
Lohrmann on Cybersecurity
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Latest news
Latest news
V
Visual Studio Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LINUX DO - 最新话题
Google DeepMind News
Google DeepMind News
小众软件
小众软件
NISL@THU
NISL@THU
GbyAI
GbyAI
H
Heimdal Security Blog
Jina AI
Jina AI
I
InfoQ
PCI Perspectives
PCI Perspectives
P
Privacy International News Feed
P
Proofpoint News Feed
N
News and Events Feed by Topic
C
CERT Recently Published Vulnerability Notes
aimingoo的专栏
aimingoo的专栏
SecWiki News
SecWiki News
B
Blog RSS Feed
阮一峰的网络日志
阮一峰的网络日志

重归混沌的BLOG

给silly实现了一个ernro模块 | 重归混沌的BLOG 给silly实现了一个ernro模块 | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 如何基于LanguageServerProtocol来编写lint工具 谈谈游戏服务器中RPC模块的设计 谈谈游戏服务器代码抽象 最近碰到的一个分布式一致性问题 谈谈游戏服务器的自动化测试 对Raft协议的一点理解 使用mmap来学习/proc/pid/smaps 2023(完) 再次实现了一个Lua性能分析器 终于给Silly的定时器增加了取消功能 一次虚拟内存排查经历 为silly增加了互斥锁 2022(完) Go语言之闭包篇 一例误用unsafe包引起的内存问题 Go语言之内存篇 初识Go语言 重新抽象图形API 给Lua实现了一个数学库 谈谈跨平台图形API的抽象 寻路和Flocking算法的结合 行为树的一种高效实现 内测过程中Shader出现的问题 彻底解决多国语言 谈谈数据库的选型 再谈Lua热更新(终) 初窥Rust 关于游戏服务器的服务拆分 ECS的初步实现 ECS初探 屏幕空间(SreenSpace)的想象力 一些对辐射度量学的理解 深度缓冲和半透明渲染 Mysql的间隙锁 更新一些GPU相关知识 2020 地形渲染之爬过的坑 Lua5.3 GC源码阅读(5) 实现一个数据库存储队列 再学计算机图形学入门 再谈分布式服务架构 游戏上线一个月后的反思 一次并发Bug 双向链表的三种实现 谈谈随机数的使用 再谈性能优化 2019 Lua中的函数式编程 重构登录逻辑 Unity资源管理(续) 谈谈Unity的资源管理 一次关于Cache的性能分析 历史之2018 DC3算法 移动平台native代码遭遇的坑 从CPU层面谈谈优化 开卷有益(UNIX编程艺术篇) GC竞争问题 通过Mesh投影来实现贴花系统 谈谈我对数据同步的理解 又一个类型提升引起的Bug Lua5.3 GC源码阅读(4) Lua5.3 GC源码阅读(3) Lua5.3 GC源码阅读(2) Lua5.3 GC源码阅读(1) 三角形光栅化时遇到的坑 一次git事故 再见2017 又一个lua调试器 客户端缓存落地方案 Paxos算法 HTTP服务器的特点 一次性能优化经历 关于CPU分支预测 C程序中让两个不同版本的库共存 实现了一个AOI模块 一个高可伸缩的游戏服务器架构 关于网络协议封装的一些新想法
游戏服务器分布式数据的一种同步的思路
重归混沌 · 2023-07-01 · via 重归混沌的BLOG

在游戏服务器的分布式设计中,我们通常避免将密切交互或存在数据强耦合的两个模块分别实现在不同的进程中。

然而,在某些情况下,我们不得不通过RPC在不同进程之间进行通信。例如,在处理玩家请求时,A进程可能需要与B进程进行资源扣除,并继续执行逻辑。

由于网络的不稳定性,A进程向B进程发起的RPC可能会有三种可能的结果:成功、失败或超时。

在分布式设计中,超时问题是最具挑战性的,因为超时可能是由于B进程未收到请求,也可能是B进程执行后的响应未被A进程接收到。

因此,在重试超时的RPC时必须非常小心,因为有可能这个RPC已经执行成功了,只是响应包丢失了而已,这样就会多次扣除玩家资源。

幸运的是,同一个游戏服务器的一组进程通常位于同一个子网下,并且在停服维护期间也是同生共死的。因此,RPC超时的概率通常不会很大,除非服务器超载。

在这种前提下,针对RPC的超时情况,一般的做法是在调用方和被调用方打上详细的日志。如果真的发生了超时,可以根据日志给玩家进行相应的补偿。


最近我遇到了一个有点棘手的需求,经过简化后的内容是这样的:

我们需要在"跨服"给玩家定期(大概几十秒到几百秒不等)产出一定的资源到"本地服",这个产出的资源需要玩家手动领取。在玩家没有领取之前,只能暂存N个周期的资源产出,到达暂存上限后就停止产出,直到玩家手动进行领取之后,再继续产出。

这个需求的棘手之处在于,在维护时,"跨服"的生命周期很大程度上和"本地服"不一样,也就是说数据包丢失必然会发生,被丢包的玩家必然会丢失这一周期产出的资源。

这种情况下,就不能只打印日志了,因为这是一种必然会发生的事件,而且影响范围是所有玩家,每次维护后手动处理是不能接受的。

经过仔细思考后,我认为要将"跨服产出到本地服"这个行为拆成两部分来实现。

第一步是先将资源产出到"跨服",这一步在进程内完成,如果跨服进程被关闭,就直接不会产出,下次启动后根据时间戳可以自动恢复出相应需要产出的资源。

第二步是将"跨服"进程中暂存的所有资源同步到"本地服"。同步数据包可能会丢失,但是由于每次总是同步当前玩家暂存的所有资源,所以重试这个同步操作是幂等的。

如果仅仅是产出重试这一问题,到这里问题基本上已经解决了。但是,现实往往是复杂的。

玩家领取资源时,还需要将产出的资源扣除以便"跨服"可以继续产出,而只要行为是"扣除",无论怎么设计这个RPC请求都不可能被重试。

所以需要进一步改进"跨服"产出逻辑。

"跨服"记录的不再是暂存的资源数量,而是从玩家参与这个玩法时到现在一共产出的"总"产出周期(TotalCycle)和"总"产出资源(TotalResource),这两个值都是只增不减的。

"本地服"同样记录收从"跨服"收到的最新的TotalCycle和TotalResource。

"本地服"每次收到"同步"包之后,根据同步包中的TotalCycle和TotalResource减去"本地服"中的TotalCycle和TotalResource,就能得出本次同步一共产出了多少周期和多少资源。再将本次同步产出的周期和资源合并到"本地服"的暂存周期数(StashCycle)和暂存产出资源数(StashResource)。

当玩家领取奖励时,"本地服"只需要将存储的TotalCycle和StashCycle同步到"跨服",跨服就可以知道当前还有多少周期的产出资源未被领取。具体公式为:(StashCycle + 跨服.TotalCycle – 本地服.TotalCycle)。

这样在玩家领取奖励时,从扣除暂存资源到加入玩家背包又是在进程内完成的,通知跨服可以继续产出的行为又变成了幂等的。


在整个问题的解决思路中,其实无非就是一点。将一些分布式行为拆分成本地行为+ "同步"行为(同步是指幂等的行为),在拆分过程中,"单调"会是一个很有用的武器。