游戏服务器的 HTTP API 层:为什么我们选择 C++ 而非 Go

游戏服务器的 HTTP API 层:为什么我们选择 C++ 而非 Go 游戏服务器迟早要暴露 HTTP API,问题不是"要不要",而是怎么加。单独起一个 Go/Python sidecar?还是直接嵌进 C++ 进程?十年以上游戏服务器开发经验告诉我——后者往往是更务实的选择。本文结合 Hical 框架的实践,聊聊背后的取舍。 游戏服务器需要 HTTP API 吗? 很多人的第一反应是"游戏用的是自定义 TCP 协议,要 HTTP 干嘛"。但在实际运营中,HTTP API 的需求无处不在: 运营 GM 工具:封号、解封、发补偿道具、改玩家数据。运营人员不会连服务器敲命令,他们需要一个 Web 界面,背后是 HTTP API。 充值回调:支付平台(微信支付、支付宝、Apple IAP)在用户付款成功后,会用 HTTP POST 通知你的服务器,这个通知必须落到游戏服务器上,否则如何给玩家加钻石? 公告系统:运营在 CMS 后台写好公告,需要一个接口通知游戏服务器"有新公告了,推给在线玩家"。 排行榜 / 战报分享:玩家把战报链接发给朋友,朋友点开是个 H5 页面,数据从游戏服务器的 HTTP 接口来。 健康检查:K8s 的 readinessProbe、运维监控系统(Prometheus、Zabbix)都期望一个 GET /health 端点,返回 200 就代表进程活着。 这些场景加在一起,游戏服务器没有 HTTP API 几乎无法正常运营。 为什么不单独起一个 Go 服务? “那我单独用 Go 写个 HTTP 服务不行吗?” 行,但会带来一系列麻烦。 ...

May 2, 2026 · 6 min · 1179 words

C++20 实战心得:现代 C++ 真正成熟的一代

C++20 实战心得:现代 C++ 真正成熟的一代 C++11 是革命,C++17 是打磨,C++20 是让 C++ 终于像一门「现代语言」。 写在前面 如果说 C++17 的升级是务实的,那 C++20 就是一次结构性的飞跃。协程、Concepts、Ranges、Modules——每一个都是重量级特性。但老实说,截至 2026 年,并非所有特性都已经在生产环境中稳定好用。 这篇文章从我在游戏服务器和 Hical 框架开发中的实际使用出发,聊聊哪些 C++20 特性已经值得用、哪些还需要等等。 一、Concepts —— 模板错误信息终于能看懂了 1.1 C++20 之前的模板报错 先感受一下 C++17 时代的"恐怖": 1 2 std::list<int> lst; std::sort(lst.begin(), lst.end()); GCC 会喷出几十行模板展开错误,核心意思是 std::list::iterator 不是随机访问迭代器——但你得从一堆 __normal_iterator、__gnu_cxx 嵌套模板中自己悟出来。 1.2 Concepts:把约束说人话 1 2 3 4 5 6 7 8 9 template <std::random_access_iterator Iter> void mySort(Iter first, Iter last) { // ... } std::list<int> lst; mySort(lst.begin(), lst.end()); // 错误信息:约束 'random_access_iterator' 不满足 // 一行,清清楚楚 Concepts 的本质:给模板参数加上编译期的「类型契约」。SFINAE 能做的它都能做,但写法是人能读懂的。 ...

April 20, 2026 · 8 min · 1683 words

C++17 实战心得:那些真正改变我写代码方式的特性

C++17 实战心得:那些真正改变我写代码方式的特性 从游戏服务器开发的视角出发,不求面面俱到,只聊那些真正让我「回不去了」的 C++17 特性。 写在前面 C++17 的特性列表很长,但实际工作中高频使用的并不多。这篇文章只聊我在游戏服务器开发中真正用上了、且明显感到提升的特性,按「爽度」排序。 一、结构化绑定(Structured Bindings) 1.1 告别 .first / .second C++17 之前,遍历 std::map 是这样的: 1 2 3 4 5 for (auto it = playerMap.begin(); it != playerMap.end(); ++it) { auto playerId = it->first; auto& player = it->second; // ... } C++17 之后: 1 2 3 for (auto& [playerId, player] : playerMap) { LOG_DEBUG << "玩家 " << playerId << " 等级: " << player.level; } 一行搞定,变量名直接表达语义,可读性提升巨大。 1.2 配合 insert / emplace 的返回值 1 2 3 4 auto [iter, success] = onlinePlayers.emplace(playerId, std::move(session)); if (!success) { LOG_WARN << "玩家 " << playerId << " 重复登录"; } 比起 result.second 去判断是否插入成功,success 的语义一目了然。 1.3 多返回值函数 1 2 // 解析网络包头:返回包类型和包体长度 auto [msgType, bodyLen] = parsePacketHeader(buffer); 不用再纠结「该用 std::pair 还是定义一个临时结构体」的问题了。当然,如果返回值超过 3 个,还是老老实实定义结构体。 ...

April 19, 2026 · 5 min · 1014 words

深入学习 ODB(八):实战——构建完整的游戏存档系统

系列导航:编译器与注解 | 连接与事务 | 对象关系 | 类型安全查询 | 继承与视图 | 迁移与多库 | 性能与实践 | 实战项目(本文) 引言:从零到完整 经过前七篇的学习,我们掌握了 ODB 的所有核心能力。本篇将综合运用它们,构建一个完整可运行的游戏存档系统——这不是简化的教学示例,而是接近生产级别的架构。 我们要实现以下游戏系统: 1 2 3 4 5 6 7 ┌─────────────────────────────────────────────────────────┐ │ 游戏存档系统 │ ├─────────────┬──────────────┬────────────┬───────────────┤ │ 玩家系统 │ 背包系统 │ 公会系统 │ 交易系统 │ │ Player │ Inventory │ Guild │ TradeLog │ │ Profile │ Equipment │ Members │ AuditLog │ └─────────────┴──────────────┴────────────┴───────────────┘ 1. 需求分析与数据建模 1.1 E-R 关系图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ┌──────────────┐ │ PlayerProfile│ │ (1:1) │ └──────┬───────┘ │ ┌──────────┐ ┌──────┴───────┐ ┌──────────────┐ │ Equipment│◀── 1:N ┤ Player ├ M:N ──▶│ Guild │ │ (装备栏) │ │ │ │ │ └──────────┘ └──────┬───────┘ └──────────────┘ │ ┌──────┴───────┐ │InventoryItem │ │ (1:N 背包) │ └──────────────┘ ┌──────────────┐ │ TradeLog │ ← 独立审计表 └──────────────┘ 1.2 实体清单 实体 说明 关键关系 BaseEntity 抽象基类,提供 id / createdAt / updatedAt 所有实体继承 Player 玩家核心数据 一对一 Profile,一对多背包/装备,多对多公会 PlayerProfile 玩家扩展档案 反向引用 Player InventoryItem 背包道具 多对一 Player Equipment 装备栏(穿戴中的装备) 多对一 Player Guild 公会 多对多 Player TradeLog 交易日志 独立表,通过 playerId 关联 2. ODB 对象定义 2.1 抽象基类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 // db/objects/base_entity.hxx #ifndef BASE_ENTITY_HXX #define BASE_ENTITY_HXX #include <string> #include <cstdint> #include <odb/core.hxx> // 所有实体的公共字段 #pragma db object abstract class BaseEntity { public: uint64_t id() const { return id_; } const std::string& createdAt() const { return createdAt_; } const std::string& updatedAt() const { return updatedAt_; } void setCreatedAt(const std::string& t) { createdAt_ = t; } void setUpdatedAt(const std::string& t) { updatedAt_ = t; } protected: friend class odb::access; #pragma db id auto uint64_t id_{0}; #pragma db type("DATETIME") column("created_at") not_null std::string createdAt_; #pragma db type("DATETIME") column("updated_at") not_null std::string updatedAt_; }; #endif 2.2 玩家与档案 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 // db/objects/player_profile.hxx #ifndef PLAYER_PROFILE_HXX #define PLAYER_PROFILE_HXX #include <string> #include <odb/core.hxx> #include <odb/lazy-ptr.hxx> #include "base_entity.hxx" class Player; #pragma db object table("player_profile") class PlayerProfile : public BaseEntity { public: PlayerProfile() = default; PlayerProfile(const std::string& avatar, const std::string& signature) : avatar_(avatar), signature_(signature) {} const std::string& avatar() const { return avatar_; } const std::string& signature() const { return signature_; } int totalKills() const { return totalKills_; } int totalDeaths() const { return totalDeaths_; } int totalPlayTime() const { return totalPlayTime_; } void setAvatar(const std::string& a) { avatar_ = a; } void setSignature(const std::string& s) { signature_ = s; } void addKill() { ++totalKills_; } void addDeath() { ++totalDeaths_; } void addPlayTime(int seconds) { totalPlayTime_ += seconds; } odb::lazy_weak_ptr<Player>& owner() { return owner_; } private: friend class odb::access; #pragma db inverse(profile_) odb::lazy_weak_ptr<Player> owner_; #pragma db type("VARCHAR(256)") std::string avatar_; #pragma db type("VARCHAR(128)") std::string signature_; #pragma db column("total_kills") not_null int totalKills_{0}; #pragma db column("total_deaths") not_null int totalDeaths_{0}; #pragma db column("total_play_time") not_null int totalPlayTime_{0}; // 累计在线秒数 }; #endif 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 // db/objects/player.hxx #ifndef PLAYER_HXX #define PLAYER_HXX #include <string> #include <vector> #include <map> #include <memory> #include <cstdint> #include <odb/core.hxx> #include <odb/lazy-ptr.hxx> #include <odb/section.hxx> #include "base_entity.hxx" class PlayerProfile; class InventoryItem; class Equipment; class Guild; // 玩家状态枚举 enum class PlayerStatus : int { Active = 0, // 正常 Banned = 1, // 封禁 Deleted = 2 // 已删除(软删除) }; #pragma db model version(1, 1) #pragma db object table("player") class Player : public BaseEntity { public: Player() = default; Player(const std::string& name, int level = 1) : name_(name), level_(level) {} // --- 核心字段访问 --- const std::string& name() const { return name_; } int level() const { return level_; } uint64_t exp() const { return exp_; } uint64_t gold() const { return gold_; } PlayerStatus status() const { return status_; } void setName(const std::string& n) { name_ = n; } void setLevel(int lv) { level_ = lv; } void addExp(uint64_t e) { exp_ += e; } void addGold(uint64_t g) { gold_ += g; } void deductGold(uint64_t g) { gold_ -= g; } void setStatus(PlayerStatus s) { status_ = s; } // --- 关系访问 --- odb::lazy_shared_ptr<PlayerProfile>& profile() { return profile_; } std::vector<odb::lazy_shared_ptr<InventoryItem>>& inventory() { return inventory_; } std::vector<odb::lazy_shared_ptr<Equipment>>& equipments() { return equipments_; } std::vector<odb::lazy_shared_ptr<Guild>>& guilds() { return guilds_; } // --- 扩展段访问 --- odb::section& skillSection() { return skillSection_; } const std::map<uint64_t, int>& skills() const { return skills_; } void setSkillLevel(uint64_t skillId, int level) { skills_[skillId] = level; } private: friend class odb::access; // ===== 核心字段(默认加载)===== #pragma db type("VARCHAR(32)") not_null unique std::string name_; #pragma db not_null index int level_{1}; uint64_t exp_{0}; uint64_t gold_{0}; #pragma db column("vip_level") not_null int vipLevel_{0}; #pragma db not_null PlayerStatus status_{PlayerStatus::Active}; // ===== 关系映射 ===== // 一对一:玩家档案 #pragma db not_null odb::lazy_shared_ptr<PlayerProfile> profile_; // 一对多:背包道具 #pragma db inverse(owner_) value_not_null std::vector<odb::lazy_shared_ptr<InventoryItem>> inventory_; // 一对多:装备栏 #pragma db inverse(wearer_) value_not_null std::vector<odb::lazy_shared_ptr<Equipment>> equipments_; // 多对多:加入的公会 #pragma db inverse(members_) value_not_null std::vector<odb::lazy_shared_ptr<Guild>> guilds_; // ===== 扩展段(按需加载)===== #pragma db load(lazy) update(change) section(skillSection_) \ table("player_skills") key_column("skill_id") value_column("level") std::map<uint64_t, int> skills_; #pragma db transient odb::section skillSection_; }; #endif 2.3 背包与装备 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 // db/objects/inventory_item.hxx #ifndef INVENTORY_ITEM_HXX #define INVENTORY_ITEM_HXX #include <cstdint> #include <odb/core.hxx> #include <odb/lazy-ptr.hxx> #include "base_entity.hxx" class Player; // 道具品质 enum class ItemQuality : int { White = 1, Green = 2, Blue = 3, Purple = 4, Orange = 5 }; #pragma db object table("inventory") class InventoryItem : public BaseEntity { public: InventoryItem() = default; InventoryItem(uint64_t templateId, int count, int slot, ItemQuality quality) : templateId_(templateId), count_(count), slot_(slot), quality_(quality) {} uint64_t templateId() const { return templateId_; } int count() const { return count_; } int slot() const { return slot_; } ItemQuality quality() const { return quality_; } void setCount(int c) { count_ = c; } void addCount(int n) { count_ += n; } void setSlot(int s) { slot_ = s; } odb::lazy_shared_ptr<Player>& owner() { return owner_; } private: friend class odb::access; #pragma db not_null odb::lazy_shared_ptr<Player> owner_; #pragma db column("template_id") not_null index uint64_t templateId_{0}; #pragma db not_null int count_{1}; #pragma db not_null int slot_{0}; #pragma db not_null ItemQuality quality_{ItemQuality::White}; }; #endif 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 // db/objects/equipment.hxx #ifndef EQUIPMENT_HXX #define EQUIPMENT_HXX #include <cstdint> #include <odb/core.hxx> #include <odb/lazy-ptr.hxx> #include "base_entity.hxx" class Player; // 装备部位 enum class EquipSlot : int { Head = 0, Chest = 1, Legs = 2, Feet = 3, MainHand = 4, OffHand = 5, Ring = 6, Necklace = 7 }; #pragma db object table("equipment") class Equipment : public BaseEntity { public: Equipment() = default; Equipment(uint64_t templateId, EquipSlot slot, int attack, int defense, int enhanceLevel) : templateId_(templateId), slot_(slot), attack_(attack), defense_(defense), enhanceLevel_(enhanceLevel) {} uint64_t templateId() const { return templateId_; } EquipSlot slot() const { return slot_; } int attack() const { return attack_; } int defense() const { return defense_; } int enhanceLevel() const { return enhanceLevel_; } int maxEnhance() const { return maxEnhance_; } void setEnhanceLevel(int lv) { enhanceLevel_ = lv; } void addAttack(int a) { attack_ += a; } void addDefense(int d) { defense_ += d; } odb::lazy_shared_ptr<Player>& wearer() { return wearer_; } private: friend class odb::access; #pragma db not_null odb::lazy_shared_ptr<Player> wearer_; #pragma db column("template_id") not_null uint64_t templateId_{0}; #pragma db not_null EquipSlot slot_{EquipSlot::MainHand}; int attack_{0}; int defense_{0}; #pragma db column("enhance_level") not_null int enhanceLevel_{0}; #pragma db column("max_enhance") not_null int maxEnhance_{15}; }; #endif 2.4 公会 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 // db/objects/guild.hxx #ifndef GUILD_HXX #define GUILD_HXX #include <string> #include <vector> #include <cstdint> #include <odb/core.hxx> #include <odb/lazy-ptr.hxx> #include "base_entity.hxx" class Player; #pragma db object table("guild") class Guild : public BaseEntity { public: Guild() = default; Guild(const std::string& name, uint64_t leaderId) : name_(name), leaderId_(leaderId) {} const std::string& name() const { return name_; } uint64_t leaderId() const { return leaderId_; } int level() const { return level_; } const std::string& announcement() const { return announcement_; } void setLevel(int lv) { level_ = lv; } void setLeaderId(uint64_t id) { leaderId_ = id; } void setAnnouncement(const std::string& a) { announcement_ = a; } std::vector<odb::lazy_shared_ptr<Player>>& members() { return members_; } private: friend class odb::access; #pragma db type("VARCHAR(32)") not_null unique std::string name_; #pragma db column("leader_id") not_null uint64_t leaderId_{0}; #pragma db not_null int level_{1}; #pragma db column("max_members") not_null int maxMembers_{50}; #pragma db type("VARCHAR(256)") std::string announcement_; #pragma db value_not_null unordered table("guild_members") std::vector<odb::lazy_shared_ptr<Player>> members_; }; #endif 2.5 交易日志 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 // db/objects/trade_log.hxx #ifndef TRADE_LOG_HXX #define TRADE_LOG_HXX #include <string> #include <cstdint> #include <odb/core.hxx> #include "base_entity.hxx" enum class TradeAction : int { Buy = 1, // 商店购买 Sell = 2, // 商店出售 Transfer = 3, // 玩家间转账 Enhance = 4, // 装备强化消耗 Reward = 5 // 系统奖励 }; #pragma db object table("trade_log") class TradeLog : public BaseEntity { public: TradeLog() = default; TradeLog(uint64_t playerId, TradeAction action, uint64_t itemId, int quantity, uint64_t goldChange, const std::string& detail) : playerId_(playerId), action_(action), itemId_(itemId), quantity_(quantity), goldChange_(goldChange), detail_(detail) {} uint64_t playerId() const { return playerId_; } TradeAction action() const { return action_; } uint64_t itemId() const { return itemId_; } int quantity() const { return quantity_; } uint64_t goldChange() const { return goldChange_; } const std::string& detail() const { return detail_; } private: friend class odb::access; #pragma db column("player_id") not_null index uint64_t playerId_{0}; #pragma db not_null TradeAction action_{TradeAction::Buy}; #pragma db column("item_id") uint64_t itemId_{0}; int quantity_{0}; #pragma db column("gold_change") uint64_t goldChange_{0}; #pragma db type("VARCHAR(256)") std::string detail_; }; #endif 2.6 View 定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 // db/views/game_views.hxx #ifndef GAME_VIEWS_HXX #define GAME_VIEWS_HXX #include <string> #include <cstdint> #include <odb/core.hxx> #include "db/objects/player.hxx" #include "db/objects/guild.hxx" #include "db/objects/trade_log.hxx" // 排行榜条目 #pragma db view object(Player) struct LeaderboardEntry { #pragma db column(Player::id_) uint64_t id; #pragma db column(Player::name_) std::string name; #pragma db column(Player::level_) int level; #pragma db column(Player::exp_) uint64_t exp; }; // 全服统计 #pragma db view object(Player) struct ServerStats { #pragma db column("count(" + Player::id_ + ")") uint64_t totalPlayers; #pragma db column("avg(" + Player::level_ + ")") double avgLevel; #pragma db column("max(" + Player::level_ + ")") int maxLevel; #pragma db column("sum(" + Player::gold_ + ")") uint64_t totalGold; }; // 公会战力排名 #pragma db view object(Guild) object(Player: Guild::members_) struct GuildPowerRank { #pragma db column(Guild::id_) uint64_t guildId; #pragma db column(Guild::name_) std::string guildName; #pragma db column("count(" + Player::id_ + ")") int memberCount; #pragma db column("sum(" + Player::level_ + ")") int totalPower; }; // 每日交易汇总 #pragma db view query( \ "SELECT DATE(created_at) AS trade_date, " \ " action AS trade_action, " \ " COUNT(*) AS trade_count, " \ " SUM(gold_change) AS total_gold " \ "FROM trade_log " \ "GROUP BY DATE(created_at), action " \ "ORDER BY trade_date DESC, action ASC") struct DailyTradeReport { #pragma db column("trade_date") type("DATE") std::string tradeDate; #pragma db column("trade_action") int tradeAction; #pragma db column("trade_count") uint64_t tradeCount; #pragma db column("total_gold") uint64_t totalGold; }; #endif 3. Repository 层实现 Repository 层封装所有数据库操作,让业务层不直接依赖 ODB 类型。 ...

July 12, 2025 · 24 min · 4920 words

Lua 热更机制深度研究:从底层原理到线上框架

Lua 热更机制深度研究 适合读者:需要处理 Lua 热更新逻辑的服务端/客户端开发者 本文以 Lua 5.3/5.4 为基础,结合游戏服务器实际场景 第一章:为什么要热更? 1.1 需求场景 MMORPG 服务器是 7×24 小时运行的,不能随便重启。但业务逻辑天天都要改: 1 2 3 4 5 6 周一:修复一个 BOSS 技能 bug 周二:调整副本掉落概率 周三:加一个限时活动 周四:修复玩家卡地图的 bug 周五:优化新手引导文本 ... 如果每次改一行 Lua 就要重启服务器,所有在线玩家都要掉线重连,体验极差。 热更(Hot Reload / Hotfix) 就是:在不重启进程的前提下,动态替换正在运行的代码。 1.2 热更的技术挑战 听起来很美好,但有几个棘手的问题: 1 2 3 4 5 6 7 8 ┌─────────────────────────────────────────────┐ │ Lua 热更要解决的 4 个核心问题 │ │ │ │ 1. 代码替换:怎么用新代码替换旧代码? │ │ 2. 状态保持:旧函数持有的数据怎么办? │ │ 3. 持有引用:旧函数还被其他地方引用怎么办? │ │ 4. 安全性:热更到一半崩溃了怎么办? │ └─────────────────────────────────────────────┘ 第二章:Lua 热更的底层原理 2.1 重新理解 Lua 的"加载文件"过程 先看一个最基本的问题:require("xxx") 到底做了什么? ...

September 20, 2022 · 12 min · 2481 words

Lua 内存泄漏排查实战:从 GC 原理到线上定位

Lua 内存分析与泄漏排查 适合读者:需要排查线上 Lua 内存泄漏、分析 GC 压力的开发者 本文以 Lua 5.3/5.4 为基础,结合游戏服务器实际场景 第一章:Lua 内存管理概览 1.1 Lua 的内存模型 Lua 的内存管理采用自动垃圾回收(GC),开发者不需要手动分配/释放内存。所有对象(Table、Function、String、Userdata)都由 GC 统一管理。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Lua 进程内存分布: ┌─────────────────────────────────────┐ │ C 层面分配(lua_newstate) │ ← Lua State 本身 ├─────────────────────────────────────┤ │ Lua 栈(lua_stack) │ ← 每个 lua_State 的栈空间 ├─────────────────────────────────────┤ │ GC 对象(可回收) │ ← Table, Function, String, Userdata │ ├── Table │ │ ├── Function (闭包) │ │ ├── String (短字符串/长字符串) │ │ ├── Userdata │ │ └── Thread (协程) │ ├─────────────────────────────────────┤ │ GC 元数据 │ ← GC 链表的辅助结构 └─────────────────────────────────────┘ 1.2 Lua GC 的工作方式 Lua 5.3/5.4 使用三色标记-清除(Tri-color Mark-and-Sweep) 算法,分步执行: ...

September 18, 2022 · 17 min · 3599 words

Lua 性能分析工具实战:从零搭建你的 Profiling 工具箱

Lua 性能分析工具实战 适合读者:想在项目中定位 Lua 性能瓶颈、用数据说话的开发者 本文以 Lua 5.3/5.4 为基础,结合游戏服务器实际场景 第一章:为什么要做性能分析? 1.1 一个常见的困境 你写了一堆"Lua 性能优化技巧"文档,告诉你用 local、少创建 Table、重用对象……但是: 1 2 3 4 问题 1:线上服务器慢,但到底是 C++ 慢还是 Lua 慢? 问题 2:几个队友都说自己的代码"没问题",如何客观地找到最慢的? 问题 3:优化后怎么"证明"确实变快了? 问题 4:上线前怎么防止新代码引入性能回退? 答案:不要靠猜,用工具说话。 1.2 性能分析金字塔 1 2 3 4 5 6 7 8 9 10 11 ┌──────────┐ │ 线上监控 │ ← 最慢但最真实 ├──────────┤ │ 压测场景 │ ← 模拟真实负载 ├──────────┤ │ 单元分析 │ ← 分析单个函数/模块 ├──────────┤ │ CPU Prof │ ← 采样/插桩分析 ├──────────┤ │ 代码审查 │ ← 最便宜但最不准 └──────────┘ 本文从下往上,带你把每一层都跑通。 ...

September 15, 2022 · 15 min · 3063 words

Lua 性能进阶:VM 原理、Table 布局优化与 JIT 编译

Lua 性能进阶:Lua VM 原理、Table 布局优化、JIT 编译优化 适合读者:有一定 Lua 使用经验,想深入理解底层原理并写出高性能 Lua 代码的开发者 本文以 Lua 5.3 为主(项目中的 Lua 5.4 大同小异),JIT 部分以 LuaJIT 为例 第一章:Lua VM 概览 1.1 Lua 是一门什么语言? Lua 是一门动态类型、寄存器式虚拟机的语言。这个定义里最关键的是"寄存器式虚拟机"——它决定了 Lua 的执行模型。 对比一下两种虚拟机模型: 模型 代表 特点 栈式虚拟机 JVM, Python 大部分指令操作栈顶元素 寄存器式虚拟机 Lua, Dalvik 指令直接操作虚拟寄存器(类似 CPU 寄存器) 寄存器式的好处是什么?一条指令能干更多事。比如 a = b + c,Lua 编译器会生成一条 ADD R(a) R(b) R(c) 指令,直接完成;而栈式可能需要 push b; push c; add; pop a 四条指令。 1.2 Lua 程序是怎么跑起来的? 1 源代码 → 词法分析 → 语法分析 → 字节码生成 → VM 执行 我们写一段 Lua 代码,看看它编译成什么样子: ...

September 10, 2022 · 27 min · 5724 words

Lua 与 C++ 绑定层原理:从 LuaBridge 看透跨语言调用

Lua 与 C++ 绑定层原理 适合读者:需要在 C++ 服务器中开发 Lua 绑定的开发者 本文以 LuaBridge 2.7 为例,结合项目实际场景 第一章:为什么需要绑定层? 1.1 C++ 和 Lua 各自的优势 语言 优势 在项目中的角色 C++ 高性能、系统级控制、内存管理 网络通信、数据持久化、战斗计算、AOI、实体组件 Lua 热更新、灵活、开发效率高 业务逻辑、活动副本、任务系统、NPC 交互 绑定层(Binding Layer)就是架在 C++ 和 Lua 之间的桥: 1 2 3 4 5 Lua 代码 ↓ 调用 绑定层(LuaBridge) ↓ 转发 C++ 函数/对象 通过绑定层,Lua 代码可以: 调用 C++ 全局函数 创建和使用 C++ 类对象 访问 C++ 类的成员变量和方法 注册 C++ 回调到 Lua 1.2 一个绑定例子 1 2 3 4 5 6 7 8 9 10 11 // C++ 类 class Player { public: int GetLevel() const { return m_nLevel; } void SetLevel(int level) { m_nLevel = level; } void AddExp(int exp); private: int m_nLevel = 1; int m_nExp = 0; }; 通过 LuaBridge 绑定后,Lua 里这样用: ...

September 1, 2022 · 16 min · 3374 words