游戏后端分布式学习——分布式锁与选主
概念为何引入分布式锁分布式锁是最直接、最成熟的方案尤其适合短时间、高频次的互斥操作对一致性要求极高不能出现重复奖励需要跨服务器协调分布式锁在mmo游戏中常见场景简易分布式锁实现用于后续场景中分布式锁的使用importtimeimportuuidimportredisclassDistributedLock:def__init__(self,redis_client,lock_key,expire10):self.redisredis_client self.lock_keylock_key self.expireexpire self.owner_idstr(uuid.uuid4())# 唯一标识持有者defacquire(self):尝试获取锁成功返回 True失败返回 False# SET key value NX EX seconds —— 原子操作resultself.redis.set(self.lock_key,self.owner_id,nxTrue,exself.expire)returnresultisnotNone# Redis 返回 OK 表示成功defrelease(self):释放锁只释放属于自己的锁# 使用 Lua 脚本保证原子性检查 owner 再删除lua_script if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end self.redis.eval(lua_script,1,self.lock_key,self.owner_id)def__enter__(self):self.acquire()returnselfdef__exit__(self,exc_type,exc_val,exc_tb):self.release()限量活动发放全服只发 100 份限量皮肤10 万玩家同时抢——需要锁保证发到第 100 份时立即停止。场景全服限量 100 份绝版坐骑10 万玩家同时抢 并发极高瞬间万级 QPS 幂等重复发放意味着超发资损必须用分布式锁 Lua 原子扣减-- 限量活动发放的原子操作locallua_script[[ local remain_key KEYS[1] local lock_key KEYS[2] local token ARGV[1] local player_id ARGV[2] local item_id ARGV[3] -- 尝试获取锁 if redis.call(SET, lock_key, token, NX, EX, 5) then local remain redis.call(GET, remain_key) if remain and tonumber(remain) 0 then redis.call(DECR, remain_key) -- 发放物品这里简化实际需要调用游戏服 API redis.call(LPUSH, item_grant_queue, player_id .. : .. item_id) redis.call(DEL, lock_key) return 1 -- 成功 else redis.call(DEL, lock_key) return 0 -- 已发完 end else return -1 -- 获取锁失败 end ]]世界 Boss 击杀判定假设游戏里有 3 台战斗服务器分别承载不同区域的玩家。一只世界 Boss 血条归零的那一刻三个服务器都可能检测到自己区域内的某个玩家打出了“最后一击”。如果没有锁服务器 A 判定玩家 X 击杀 → 发放奖励服务器 B 也判定玩家 Y 击杀 → 同样发放奖励服务器 C 也可能凑热闹……因为“发放奖励”这个操作必须是互斥的——同一时刻只允许一台服务器执行。分布式锁正是用来在多个独立进程之间协调对共享资源的独占访问的完美工具。设计要点锁的粒度按 Boss 实例 ID 加锁比如 lock:boss:worldboss_001不同 Boss 互不影响。锁的超时设置合理超时比如 10 秒防止拿到锁的服务器突然宕机导致死锁。锁的持有者标识每个服务器生成唯一 ID如 server_A释放锁时检查是不是自己的锁避免误删别人的锁。原子性操作加锁和设超时要一起完成Redis 的 SET key value NX EX seconds。为什么不直接用数据库事务数据库事务比如 MySQL 行锁也可以做到互斥但性能开销大高并发下数据库会成为瓶颈。数据库连接可能不稳定事务超时回滚复杂。分布式环境下多个服务器可能连不同的数据库实例分库分表事务无法跨库。而 Redis 等内存型锁性能极高适合高频短时间的互斥操作。defworld_boss_kill_handler(boss_id,server_name): 模拟世界 Boss 击杀处理 boss_id: 世界 Boss 的唯一标识 server_name: 当前服务器的名字比如 Server-A rredis.Redis(hostlocalhost,port6379,decode_responsesTrue)lockDistributedLock(r,flock:boss:{boss_id},expire10)iflock.acquire():try:print(f[{server_name}] 抢到了 Boss{boss_id}的锁开始发放奖励...)# 这里执行真正的奖励发放逻辑检查击杀者、发邮件等time.sleep(1)# 模拟耗时操作print(f[{server_name}] 奖励发放完毕释放锁。)finally:lock.release()else:print(f[{server_name}] 没抢到锁放弃处理。)# 模拟三个服务器同时尝试处理importthreading threads[]fornamein[Server-A,Server-B,Server-C]:tthreading.Thread(targetworld_boss_kill_handler,args(worldboss_001,name))threads.append(t)t.start()fortinthreads:t.join()跨服匹配跨服匹配池是一个全局队列里面排着来自不同服务器的玩家。多个匹配服务器Match Server同时从这个队列里取人组队。如果没有锁匹配服务器 A 取了玩家 P1、P2准备组队。匹配服务器 B 也取了玩家 P1、P3准备组队。结果 P1 同时出现在两个队伍里游戏逻辑错乱。从匹配池取玩家这个操作必须是原子的——要么整个取走要么不取。分布式锁可以保证同一时刻只有一个匹配服务器在执行“取出并标记”的操作。设计要点锁的粒度按匹配池 ID 加锁比如 lock:matching:pool_1也可以更细粒度到“批次”。锁的持有时间匹配操作很快毫秒级锁超时可设为 1~2 秒。配合队列原子操作实际项目中常用 Redis 的 LPOP/RPOP 或 BLPOP 结合 Lua 脚本但分布式锁提供了额外的安全边界。防重复匹配拿到锁后还要二次校验玩家是否已被匹配双重检查模式。为什么不直接用 Redis 事务或 Lua 脚本Lua 脚本确实可以实现原子性但如果脚本执行时间较长比如涉及多个键操作会阻塞 Redis 主线程影响其他请求。而分布式锁可以让业务逻辑在应用层完成锁只保证互斥不阻塞 Redis。更常见的做法是用 Lua 脚本原子性地 pop 玩家再配合分布式锁防止多个匹配服务器同时执行该脚本。defmatch_players(match_pool_key,server_name): 从匹配池中取出一对玩家进行匹配 match_pool_key: Redis 列表的 key存放等待匹配的玩家 ID server_name: 当前匹配服务器名称 rredis.Redis(hostlocalhost,port6379,decode_responsesTrue)lockDistributedLock(r,flock:matching:{match_pool_key},expire2)iflock.acquire():try:# 拿到锁后从队列中取出两个玩家如果有player1r.lpop(match_pool_key)ifplayer1isNone:print(f[{server_name}] 匹配池为空无事可做。)returnplayer2r.lpop(match_pool_key)ifplayer2isNone:# 如果只取到一个放回去保持公平r.rpush(match_pool_key,player1)print(f[{server_name}] 匹配池人数不足将{player1}放回。)return# 执行匹配逻辑创建对局等print(f[{server_name}] 成功匹配{player1}和{player2})# 实际项目还会把匹配结果写入数据库finally:lock.release()else:print(f[{server_name}] 没抢到锁稍后重试。)# 模拟先向匹配池插入一些玩家rredis.Redis(hostlocalhost,port6379,decode_responsesTrue)r.delete(matching:pool_1)r.rpush(matching:pool_1,Player1,Player2,Player3,Player4)# 三个匹配服务器并发工作threads[]fornamein[Match-Server-1,Match-Server-2,Match-Server-3]:tthreading.Thread(targetmatch_players,args(matching:pool_1,name))threads.append(t)t.start()fortinthreads:t.join()选主在游戏中的常见场景

相关新闻

黑苹果BCM94360Z4蓝牙固件魔改:实现原生级AirDrop与睡眠唤醒

黑苹果BCM94360Z4蓝牙固件魔改:实现原生级AirDrop与睡眠唤醒

1. 项目概述:从“能用”到“完美”的最后一公里折腾黑苹果的朋友,对博通(Broadcom)的无线网卡一定不陌生。在苹果逐步转向自研芯片和定制无线模块的今天,那些被遗留在Intel平台上的“原装”博通网卡,成了黑…

2026/10/18 16:31:21 阅读更多 →
猫抓浏览器扩展终极指南:简单快速免费下载网页视频资源

猫抓浏览器扩展终极指南:简单快速免费下载网页视频资源

猫抓浏览器扩展终极指南:简单快速免费下载网页视频资源 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 猫抓(cat-catch&…

2026/10/18 11:24:31 阅读更多 →
Dify平台智能体开发实战:挑战与优化策略

Dify平台智能体开发实战:挑战与优化策略

1. 智能体与Dify平台的现状解析最近半年,AI智能体(AI Agent)突然成为技术圈的热门话题。作为从业者,我观察到几乎每周都有新的智能体框架或平台发布,而Dify作为国内较早开放的大模型应用开发平台,其智能体功…

2026/10/18 15:16:51 阅读更多 →

最新新闻

计算机网络综合实验方案:VLAN、OSPF、NAT与抓包分析全链路

计算机网络综合实验方案:VLAN、OSPF、NAT与抓包分析全链路

简介:这份华北电力大学计算机网络综合实验报告配套资料,面向计算机、网络工程等专业学生及需要完成课程设计的学习者,聚焦网络设备配置与协议分析两大实践方向。资源包内含1个doc文档,约21.91MB,完整呈现实验任务书、设…

2026/10/18 20:44:14 阅读更多 →
用Unity打造透明桌面挂件:Win32窗口与点击穿透详解

用Unity打造透明桌面挂件:Win32窗口与点击穿透详解

做桌面挂件、桌面时钟、动态壁纸这类需求时,很多人第一反应是用WPF、Electron或者Qt去写,但真到要精细控制3D渲染、粒子特效、骨骼动画的时候,这些方案一个比一个难受。绕一圈之后你会发现,用Unity来做“透明软件背景、显示桌面内…

2026/10/18 20:44:14 阅读更多 →
UE5蓝图截图实战:从HighResShot到RenderTarget完整指南

UE5蓝图截图实战:从HighResShot到RenderTarget完整指南

不是跟AI对话,下面直接放正文。这篇文章我按自己这些年做UE项目的习惯来写,聊的全是蓝图里和截屏死磕的实战经验,希望对为截图功能纠结过的同学有点用。1. 截图功能的应用场景与方案选型先说一句大实话:截图这件事,在U…

2026/10/18 20:44:14 阅读更多 →
离线部署轻量Agent:无网无卡也能跑通工具调用与自动化

离线部署轻量Agent:无网无卡也能跑通工具调用与自动化

很多人问我:不联网的电脑上,到底能不能跑出一个像样的AI应用来?在还没有真正动手之前,我也觉得这事悬。但当我花了将近两周时间,把一款开源轻量模型塞进一台没有外网的办公笔记本里,再给它套上Agent框架、接…

2026/10/18 20:44:14 阅读更多 →
Flutter鸿蒙适配必知:Platform View机制原理与踩坑实践

Flutter鸿蒙适配必知:Platform View机制原理与踩坑实践

前段时间我把一套基于 Flutter 的跨平台应用(下文就叫它模拟项目X吧)往鸿蒙生态迁移,最让我头疼的还不是改 Dart 代码,而是地图、视频这类必须依赖原生 SDK 的模块。页面能跑起来,可原生控件死活上不了屏,查…

2026/10/18 20:44:14 阅读更多 →
OSPF多区域实战:LSA类型、ABR机制与特殊区域配置

OSPF多区域实战:LSA类型、ABR机制与特殊区域配置

很多人学OSPF时,单区域配置玩得很溜,一到多区就懵了:明明就是两条network命令的事,为什么还要分骨干区、特殊区,甚至搞出一堆LSA类型?更麻烦的是,多区之后再看路由表,你会发现区域间…

2026/10/18 20:43:12 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/18 12:16:16 阅读更多 →