二八定律(Pareto):热点总是集中
线上出了个诡异的事故:一台网关机器 CPU 跑满,报警刷屏,可负责的同事查了半天,发现流量并没有暴涨——总量跟平时差不多。最后抓包一看,全网 1000 多个接口里,有 3 个接口扛走了 82% 的请求,而其中 1 个接口自己就占了一半。平时没人细看,因为总量不大;可一旦那几个热点接口抖动,整台机器就跟着抖。 这不是偶然,而是一条反复出现的规律:少数的东西,总是贡献了大部分的结果。 一、它从哪来1896 年前后,意大利经济学家 维尔弗雷多·帕累托(Vilfredo Pareto) 在研究财富与土地分配时发现:意大利大约 20% 的人口拥有约 80% 的土地。他把这个"少数占大头"的观察推广到更多领域,后人便称其为帕累托法则(Pareto Principle),俗称二八定律。 真正把它带进工程与管理世界的,是质量大师约瑟夫·朱兰(Joseph Juran)。1951 年他在《质量控制手册》中提出"关键少数与琐碎多数"(vital few and trivial many)的思想:质量缺陷并非均匀分布,少数几种缺陷类型造成了大部分损失——所以改进...
闻莺坊的歇弦师
闻莺坊是云栖城里最老的乐坊,十二名乐手,丝竹管弦样样齐整。坊里压箱底的一支曲子叫《春江月》,每逢大典必奏,二十年来没砸过一回。 可这半年,坊主沈三娘犯了愁。 一、全都在的时候好好的,缺一个就全乱《春江月》讲究的是十二个声部咬得严丝合缝。琵琶起头,洞箫接尾,中间七道弦索像一条河上的十二座桥,少一座,对面就过不去。 往常排练,坊里规矩是"人不齐不练"——十二个乐手必须全到场,少一个都不开锣。沈三娘觉得这规矩天经地义:曲子是十二个人一起磨出来的,缺了谁,怎么练? 可怪事就出在这儿。 三月里,打扬琴的小满娘回家奔丧,告了半个月假。沈三娘头一回破例,让坊里十一个人先练着。结果一开锣就乱套:原先该扬琴接的那一板,洞箫手等了两拍没等到,整段旋律像断了线的珠子,滚得满地都是。十一人练了七天,愣是没把一曲走完过一遍。 沈三娘叹气:"看来《春江月》离了谁都不行。" 六月里又试了一回。这次是弹琵琶的阿峦伤了手指,换了坊里最不起眼的小学徒顶替。小学徒琴艺不差,谱子也背得滚瓜烂熟,可一合奏就露了馅——他总在等邻座的眼神,等不到就慌,一慌就错。 沈三娘把他叫到跟前:&...
AI 新闻速递(2026-09-04)
今日摘要:OpenAI 于美东时间 9 月 3 日正式发布旗舰模型 GPT-6 Astra,官方公布 ARC-AGI-3 99.9%、ExploitBench 满分等评测数据,奥特曼宣称"欢迎进入 AGI 时代";微软财报重组,新设"智能体与基础设施"分部并首次将 Azure 单独列示;博通 Q3 AI 半导体收入 167 亿美元、同比大增 221%;奥特曼首次明确 OpenAI 将自研人形机器人;特斯拉发布量产版 Cybercab 无人出租车。以下是详细内容。 一、OpenAI 正式发布 GPT-6 Astra:ARC-AGI-3 99.9%、ExploitBench 满分,奥特曼称"欢迎进入 AGI 时代"美东时间 9 月 3 日,OpenAI 正式发布新一代旗舰模型 GPT-6 Astra,称其为公司迄今"最智能、对齐程度最高"的模型。OpenAI 表示,Astra 使用超过 10 万块 GPU 在得州 Stargate 基地完成训练,重点提升软件工程、科学研究、推理与计算机操作能力,可直接...
Lua 语言系列:1.1 一表通吃——从 table 到 metatable,一门语言如何只靠一种数据结构
你大概率没写过一行 Lua,但你的电脑每天都在跑它:Redis 的原子脚本、Nginx 的 OpenResty 插件、Wireshark 的协议解析、Neovim 的配置、魔兽世界的 UI 框架……这些八竿子打不着的软件,不约而同地选择把 Lua 嵌进去当"内脏语言"。 一门 1993 年诞生、源码只有三万多行、解释器能裁剪到 300KB 以下的小语言,凭什么被嵌入到全世界最流行的软件里? 答案藏在一个反直觉的设计里:Lua 只有一种数据结构——table(表)。数组是它,字典是它,对象是它,模块是它,连"类"都是它。这篇文章就把它拆开讲透:这一张表,到底是怎么撑起一整门语言的。 一、从哪来:为"可嵌入"而生的小语言Lua 诞生于 1993 年,作者是巴西里约热内卢天主教大学(PUC-Rio)的 Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo。"Lua"在葡萄牙语里是"月亮"的意思——这个名字本身...
Lua 语言系列:1.2 metatable 深挖——__index、__newindex 与运算符重载
你写过这样的需求吗:查不到的配置项要有默认值;这张表不许别人改;两个"向量"要能用 + 相加、用 == 比较。 在 C++ 里,这些分别对应——给类写 getter 兜底、const 或私有成员、operator+ 与 operator== 重载。在 Python 里是 __getattr__、__setattr__、__add__、__eq__。 而在 Lua 里,它们全部是同一件事:给一张表挂上一张元表(metatable),在元表里写下双下划线开头的元方法。 Lua 没有 class 关键字,也没有运算符重载语法——所有"让一张表拥有行为"的能力,都收敛到了这一张元表里。本篇把它拆到底:查、写、运算、调用、打印、销毁,每一条路径上元表站在哪里,以及那些足以让程序静默出错的坑。 元表拦在什么位置:读路径与写路径 读:t[k] rawget(t, k) 自己身上有吗? nil metatable.__index 表:去那张表找 非 nil:直接返回 返回值(读路径结束) 函数:调用 f(t, k) 都没有 → 返回 nil...
秒杀系统高并发优化实战(C++ / Drogon):1.2 为什么是 Drogon——C++ 框架横评与工业界的真实选择
上一篇 1.1 里我用五句话交代了"为什么选 Drogon",但那五句太像宣传册了。真到了面试或者技术评审上,一定会有人追问一句:"工业界到底谁在用 Drogon?" 这个问题很扎心,而且必须正面回答。这篇文章就干三件事:把 Drogon 和它的同类放到一张桌子上比,把"工业界实际用什么"讲清楚,最后交代我在这个项目上做这个选型时放弃了什么。 一、先回答最扎心的那个问题:工业界真用 Drogon 吗?诚实答案分三层。 第一层:大厂的 C++ 后端,几乎不使用"Web 框架"这个词。 你去问百度、腾讯、字节的 C++ 团队用什么,答案不会是 Drogon,也不会是 oat++,而是 RPC 框架——brpc、Sogou Workflow(及其上的 SRPC)、TARS、gRPC。原因很简单:大厂内部服务之间走的是二进制协议 + 服务发现,HTTP 只是南北向入口那一层,而入口通常由 Nginx / OpenResty / Envoy / Go 网关承担,轮不到 C++ 业务...
Amdahl 定律——从哪来、为什么、有什么用
Amdahl 定律——从哪来、为什么、有什么用 钩子:你花大价钱把训练从 1 张卡扩到 8 张,满心期待快 8 倍,结果只快了 3 倍;给服务加了 16 个线程,吞吐量却卡在 4 倍上不去。问题往往不在你并行得不好,而在那段"怎么也并行不掉"的串行代码,给整体加速焊死了一个天花板。 这条天花板,就是 Amdahl 定律。 0. 一个反直觉的事实先抛个问题:一段程序,90% 能并行、10% 必须串行。给你无限多的核,它最快能快多少倍? 直觉可能是"无限倍"——毕竟 90% 都能并行嘛。但答案冷冰冰:最多 10 倍。因为那 10% 的串行部分,无论你堆多少核,它都得一个人慢慢走,而总耗时里永远含着这一段。串行占比,就是加速比的天花板。 1. 它从哪来:1967 年,一场对"并行狂热"的泼冷水1967 年,IBM 的计算机架构师 Gene Amdahl 在 AFIPS 会议上提出这条定律(原称 Amdahl's Argument),本意是给当时方兴未艾的并行机热潮"降降温"。那时候业界流行一种乐观...
叠浪镇的掌闸人
一、九道闸,把上游的"讯号"一级级送到磨坊叠浪镇傍着一道常年丰沛的瀑溪而建,镇上最了不起的营生,是用溪水带动下游九座磨坊。可溪水不能直接灌进磨坊——中间隔着一道叫「九折渠」的水利工程:溪水先涌入最高处的闸口,再经九道串联的闸室,一层层往下递,最后那股水的"强弱",正好对应下游某座磨坊该转多快。 管这九道闸的,是姓岑的老闸师,人称岑掌闸。岑掌闸手下九名闸夫,每人守一道闸室。平日里,上游来一股水,闸夫们照老法子微调一下闸板,让水流"不增不减"地交给下一级——九道闸递下来,磨坊转得四平八稳。 可有两年,九折渠接连出了两桩怪事,一桩是涝,一桩是旱,病的却都不是同一处。 二、涝年:水越递越狂,磨坊被冲垮头一桩怪事出在大水年。那年上游来势汹汹,岑掌闸怕水不够使,叮嘱各闸" upstream 水大,你们也稍稍助它一臂之力"。九名闸夫心领神会,每人守闸时都习惯"顺手把水流再催旺一分"——这一道放它一点二成,下一道也放一点二成。 头两道闸还没什么,到了第五道,水流已经汹汹;等九道闸全递完,磨坊前那股...
AI 新闻速递(2026-09-03)
今日摘要:OpenAI 下一代旗舰 Astra 被曝采用"循环深度推理"架构,并成为首个触及公司"关键"网络安全红线(Critical)的模型;谷歌发布 Gemini 3.8 与漏洞挖掘专用版 Flash Cyber,同时宣布 TPU 迭代提速到一年两款;李飞飞 World Labs 发布多模态世界模型 Atlas 1,单图可生成 1 分钟 1440p 视频;纽约最大公立学区出台政策,K-8 全面禁用面向学生的生成式 AI;英伟达被曝接近以约 140 亿美元收购 Hugging Face。以下是详细内容。 一、OpenAI 新旗舰 Astra:以"算"换"参",并首触网络安全红线时间:9 月 2 日(The Information / TechCrunch 报道) 主体:OpenAI OpenAI 即将推出的旗舰模型 Astra 采用循环深度(recurrent-depth)推理架构:输出下一个词之前,文本在同一网络层内反复处理多轮,用更多计算换取更少参数。公开论文显示,这一路线下约 ...
秒杀系统高并发优化实战(C++ / Drogon):5.1 缓存 Key 设计规范与接口基线压测
阶段一我们打出基线 QPS≈371 / 0 超卖,根因钉死在 seckill_sku 单行锁串行化——那是写接口(下单)的问题。但同一时刻还有两个读接口在被反复调用:一万个用户同时刷新列表,MySQL 就把同一条 SELECT 执行一万遍,返回一模一样的结果。读请求和写请求是两种完全不同的动物,这一章我们专门收拾读接口。 本文是「秒杀系统(C++ / Drogon)」系列的第五章第一篇。配套资产:docs/CACHE-DESIGN.md(缓存规范全文)、src/service/CacheKeys.h(Key 唯一构造入口)、src/service/SkuCache.*(Redis 异步封装)、scripts/read-bench.sh + jmeter/read-baseline.jmx(读接口基线压测)。代码对应阶段二 v0.2.x。真实编译与压测在 WSL(i7-14650HX + 本地 MySQL + 本地 Redis)完成。 一、为什么现在才加缓存,加在哪 是什么:读接口(GET /api/seckill/list、GET /api/seckill...
秒杀系统高并发优化实战(C++ / Drogon):5.2 加 Redis 缓存:商品列表接口
4.1 我们把商品列表接口(GET /api/seckill/list)跑通了,但那时它每次都直连 MySQL。到了第五章,第一件事就是给这个被刷新最频繁的接口套一层 Redis 缓存。它的 key seckill:sku:v1:list 是「全量商品」的聚合值,是热点中的热点——值得专门写一篇讲清楚它和详情缓存在取舍上的不同。 本文是「秒杀系统(C++ / Drogon)」系列的第五章第二篇。配套代码:src/service/SeckillService.cc::listSkus/queryListFromDb、src/service/SkuCache.*、src/main.cc 缓存配置解析。代码对应阶段二 v0.2.x。Key 规范与 TTL 抖动见上一篇 5.1。 一、列表缓存:是什么、坑在哪、本质一句话 是什么:列表接口返回全部商品(LIMIT 100),结果对所有用户一致、允许短暂陈旧——教科书级的缓存对象。 坑:列表是一个全量聚合 key,任意一件 sku 成交都改变了它的内容。如果「写后失效」策略选错(把列表也一起删),在写 QPS 高时这个 key...
秒杀系统高并发优化实战(C++ / Drogon):5.4 缓存一致性:为什么先删库后删缓存,以及延迟双删
5.2 / 5.3 给读接口加了缓存,但缓存是副本——副本和真相源(MySQL)之间随时可能不一致。这一篇把缓存层最容易被问倒的问题讲透:写操作之后缓存怎么办?为什么顺序错了会出事故?单次删除还留了什么缝?延迟双删补的是哪条缝? 配套代码在 5.4 已落地为可开关的 double_delete_ms(默认关),这篇讲清它到底在防什么、以及为什么默认不开。 本文是「秒杀系统(C++ / Drogon)」系列第五章第四篇。配套代码:src/service/SkuCache.*(失效策略 + DelayDeleter 延迟删除线程)、config.json 的 cache.double_delete_ms。代码对应阶段二 v0.2.x。 一、缓存一致性:是什么、坑在哪、本质一句话 是什么:读接口走缓存后,系统里同一份数据存在两个地方——MySQL(真相)和 Redis(副本)。写操作发生的那一刻,两处就不一致了。所谓缓存一致性,就是管理"副本何时作废、何时重建、作废后会不会读到脏值"这一整套时序问题。 坑(最常见的两处):① 顺序反了——先删...
秒杀系统高并发优化实战(C++ / Drogon):5.3 加 Redis 缓存:商品详情接口(含空值哨兵防穿透)
4.2 把商品详情接口(GET /api/seckill/{skuId})跑通了,主键点查、直连 MySQL。这一篇给它套缓存,并顺手落地一个常被放到 5.6 才讲的能力——空值哨兵防缓存穿透。原因很实际:详情接口的 {skuId} 是用户可控的,攻击者拿随机 id 狂打,缓存层若不做处理,会退化成「每次都穿透到 DB」,等于没缓存。所以防穿透和详情缓存是同一道题的正反两面,一起讲。 本文是「秒杀系统(C++ / Drogon)」系列的第五章第三篇,也是 5.1/5.2/5.3 读缓存三篇的收口。配套代码:src/service/SeckillService.cc::detailSku/queryDetailFromDb、src/service/SkuCache.*(setNull/kNullValue)。代码对应阶段二 v0.2.x。Key 规范、TTL 抖动、列表缓存见 5.1/5.2。 一、详情缓存 + 空值哨兵:是什么、坑在哪、本质一句话 是什么:详情接口按 skuId 主键点查...
秒杀系统高并发优化实战(C++ / Drogon):5.5 缓存预热与雪崩:冷启动的回写风暴,和流量进来前该做的事
5.2 / 5.3 把读缓存架起来了,TTL 抖动也顺带做了——那防的是运行中的大批 key 同时过期。但还有一个更隐蔽的时刻:服务刚启动、缓存全空的那一刻。洪峰第一波请求全部 miss、全部回源、全部回写——缓存不但没挡住流量,反而给数据库来了一次"回写风暴"。这一篇讲缓存预热:为什么它是运营动作而不是服务行为,冷启动、雪崩、击穿三个词到底谁是谁,以及 5.5 落地成代码的预热端点长什么样。配套代码 POST /api/cache/warm(scripts/cache-warm.sh)在 5.5 已落地。 本文是「秒杀系统(C++ / Drogon)」系列第五章第五篇。配套代码:SeckillService::warmCache、src/main.cc 的 /api/cache/warm 路由、scripts/cache-warm.sh。代码对应阶段二 v0.2.x。 一、冷启动:是什么、坑在哪、本质一句话 是什么:进程刚 run() 起来时,Redis 里没有任何 seckill:sku:* key。此时用户洪峰正好杀到,每一个读请...
秒杀系统高并发优化实战(C++ / Drogon):5.6 防缓存穿透:空值策略——把"查无此物"也缓存下来
缓存防的是"重复查同一份数据"。但如果用户查的根本不是数据呢?商品详情接口的 {skuId} 是用户可控的,拿一个不存在的 id 狂刷,缓存里永远没有这个 key——每一次请求都穿透到 MySQL 查一个空结果。这一篇把缓存穿透讲完整:攻击者怎么打、空值策略为什么有效、TTL 定多长是安全与一致性的折中、以及它和 5.7 布隆过滤器怎么分工。代码(SkuCache::setNull 空值哨兵)早在 5.3 就随详情缓存一起落地了,本篇是把"为什么这么做"补全。 本文是「秒杀系统(C++ / Drogon)」系列第五章第六篇。配套代码:src/service/SeckillService.cc::queryDetailFromDb(查无此物 → cache_->setNull)、src/service/SkuCache.cc::setNull(__nil__ 哨兵 + 60s TTL)。代码对应阶段二 v0.2.x。 一、缓存穿透:是什么、坑在哪、本质一句话 是什么:攻击者(或手滑的用户)请求一个数据库...
秒杀系统高并发优化实战(C++ / Drogon):5.7 防缓存穿透:自实现布隆过滤器——海量随机 id 的终结者
5.6 的空值哨兵把"同一个不存在 id"挡在了 Redis 内,但它有个软肋:打过来的 id 如果每个都不一样,哨兵就要为每个不存在的 id 占一个 Redis key——内存随攻击流量线性涨。20 万商品的种子数据下,这个软肋变成现实威胁:id 是连续整数,攻击者随便枚举就能制造海量"新不存在的 id"。这一篇落地布隆过滤器:在进程内用约 1MB 内存维护"真实存在的 sku id 全集"的紧凑摘要,集合外的 id 直接 404——连 Redis 都不打。配套代码 src/service/BloomFilter.h(自实现,约 60 行核心)+ POST /api/cache/warm {"rebuild_bloom":true} 在 5.7 已落地。 本文是「秒杀系统(C++ / Drogon)」系列第五章第七篇。配套代码:src/service/BloomFilter.h、SeckillService::rebuildBloom / bloomAllows / ...
秒杀系统高并发优化实战(C++ / Drogon):5.8 多级缓存:自实现本地 LRU 做 L1
5.25.7 一直在优化"哪些读可以不打 MySQL"。但仔细想想,Redis 命中本身也有成本:一次网络往返 0.20.5ms。洪峰期所有用户刷同一批热 key,这个往返是纯开销——数据明明就在本进程里能算出来,为什么每次都要绕一圈网络?这一篇落地多级缓存:进程内加一层自实现的 LRU 做 L1,读路径变成 L1 本地内存 → L2 Redis → L3 MySQL。同时回答两个最尖锐的问题:L1 的一致性怎么保?L1 到底对什么样的流量有效? 配套代码 src/service/LocalLruCache.h(自实现)+ scripts/local-bench.sh(对比压测)在 5.8 已落地。 本文是「秒杀系统(C++ / Drogon)」系列第五章第八篇(阶段二收尾)。配套代码:src/service/LocalLruCache.h、SkuCache 内嵌 L1(读 L1→L2→L3,写/失效同步本地)、config cache.local_enabled / local_capacity。代码对应阶段二 v0.2.x。 一、为...
局部性原理——从哪来、为什么、有什么用
局部性原理——从哪来、为什么、有什么用 面试官问:为什么 Redis 查一次很快,而同样的数据放远程 MySQL 上要慢几十倍?为什么数组遍历比链表快,明明两者都是"一个接一个读"?为什么 CPU 造了三级缓存,却无论如何不肯做大一点?—— 答案都指向同一条规律:局部性原理。 0. 钩子:一个几乎不可能的缓存假设没有局部性原理,会发生一件很滑稽的事:CPU 花了几百亿造的 L1 Cache 几乎没有命中,命中率趋近于 0,缓存白装。为什么?因为缓存能起作用,靠的是"你读写过的数据,大概率很快还会再读写"——如果每次访问都落在完完全全随机的新位置,那缓存里存的旧东西永远派不上用场,装缓存等于没装。可现实是,几乎所有真实程序都有强局部性,所以缓存这条产业链才成立。局部性不是假设出来的,它是真实工作负载的结构性特征,缓存只是顺着这个特征搭的脚手架。 1. 它从哪来:Peter Denning 与工作集模型1965 到 1968 年,虚拟内存(分页)刚铺开不久,系统遇到一个棘手问题:程序一多、内存一紧张,页面就在内存和磁盘之间疯狂换入换出,系统陷...
临渊镇的合议官
一、临渊镇的刀,总在最后一关栽跟头临渊镇傍着一道深涧而建,以打宝刀出名。镇上刀铺接了单子,从选铁、折叠、淬火到开刃,前几道都由师傅亲手把关。可刀成不成器,最后一道"评刀"却不由师傅定——镇上有个老规矩:每把新刀要过"判官"这一关,判官说合格,方能入鞘出售;判官说不合格,整把回炉。 长久以来,这差事落在一个姓陶的老判官身上。陶公看刀看了三十年,眼力原本极好,可临渊镇的人渐渐发现一件怪事:同一批料、同一位师傅打的刀,陶公今日判合格的,隔日另一把竟在客人手里生生折断;而他昨日毙掉的那把"次刀",后来被人偷偷留下,竟用了十年未卷刃。 "陶公也有走眼的时候?"镇民嘀咕。陶公自己也想不通——他看刀的法子从没变过,怎么时灵时不灵。 二、一个爱抬杠的客人,点破了症结这年秋天,城里来了个收刀的客商,姓裴。裴商人在各家刀铺挑了三十把刀,一一请陶公评过,自己却偷偷另请三位老匠暗中复验。半月后他找上门,把一本账册拍在陶公案上。 "陶兄,你这三十把里,判错了九把。"裴商人笑道,"不是你眼力差,是每...
AI 新闻速递(2026-09-02)
今日摘要:"上下文工程"取代 Prompt 工程成为编程智能体新显学,同一模型仅因上下文编排差异成功率可差 20~40 个百分点;代码智能体进入"平行宇宙"测试,多沙箱并行验证修复方案;机器人基础模型走向视觉-语言-动作一体预训练的统一架构;人形机器人"租赁即服务"(RaaS)商业模式开始跑通;开源代码模型在 SWE-bench 类基准上集体追平闭源。以下是详细内容。 一、主线1. 「上下文工程」取代 Prompt 工程,成为编程智能体的新显学随着 100 万1000 万 token 的上下文窗口成为主流,社区共识正在从"怎么写一句好 prompt"转向"怎么把检索、记忆、工具调用结果组织成一段好的上下文"。新一批评测显示:同一模型、同一任务,仅因上下文里"先放需求还是先放报错""是否带历史成功补丁""工具输出是否裁剪",成功率能差出 2040 个百分点。 值得关注:这标志着 AI 编程的竞争焦点从"模型能...
秒杀系统高并发优化实战(C++ / Drogon):3.1 用户注册接口:PBKDF2 加盐哈希 + 自实现 JWT + 可吊销会话
10000 个人抢 100 件商品,最后卖出去 137 件——这句"段子"我们在表设计那篇就破了。但秒杀真正的第一道门,不是库存,是身份:你是谁?你有没有资格抢?如果你连"注册"这一步都把明文密码写进库,那后面无论 Redis 预扣、Lua 原子、MQ 削峰做得再漂亮,用户表一旦泄露就是全裸——所有"高并发安全"都建立在沙滩上。 本文是「秒杀系统(C++ / Drogon)」系列第三章(登录模块)的开篇。配套仓库 seckill-cpp。当前阶段一 v0.1.x 已落地登录 / 短信模块,目录为 sql/user_schema.sql、src/service/UserService.{h,cc}、src/controllers/UserController.{h,cc}、src/service/password.{h,cc}、src/service/Jwt.{h,cc}、src/service/SessionStore....
秒杀系统高并发优化实战(C++ / Drogon):3.10 注册安全加固:同 IP 注册频控 + 反代真实客户端 IP 解析
验证码改成自签发 / 日志模式后(3.3),"送达"不再是外部约束——注册接口等于裸奔在公网:攻击者拿一堆手机号从同一个 IP 批量注册,就能刷出成千上万个账号。这篇补两道闸门:① 同 IP 注册频控堵批量刷号;② 反代真实客户端 IP 解析,否则频控的 key 拿到的是代理 IP,等于没防。 配套代码:src/service/RegisterGuard.{h,cc}(check / markSuccess + kCheckScript / kMarkScript)、src/controllers/UserController.cc 的 clientIp()、UserService::registerUser 最前的频控闸门。阈值走 config.json 的 register_limit(max_per_ip=5 / window_seconds=3600)。 一、是什么、坑在哪、本质一句话是什么:注册链路在"手机号格式校验之前"先过一道同 IP 固定窗口频控(Registe...
秒杀系统高并发优化实战(C++ / Drogon):3.2 用户登录接口:密码比对 + 自实现 HS256 签发 + Redis 可吊销会话
注册接口建好了"身份",这一篇让身份"进门"——登录接口。它要做三件事:比对密码、签发 JWT、把会话写进 Redis。前两件是安全,第三件是"可吊销"——没有它,JWT 一旦签发就只能在 exp 到点后自己失效,改密码/封号都拦不住旧 Token,等于门没锁。 配套代码:src/service/UserService.cc(login)、src/service/Jwt.{h,cc}、src/service/SessionStore.{h,cc}、src/controllers/UserController.cc(login)。延续 3.1 的"服务层只做业务、控制器只做协议转换"分层。 一、登录接口:是什么、坑在哪、本质一句话是什么:登录 = 用"手机号 + 密码"证明你是你,成功后发一个不可伪造的凭证(JWT),并在 Redis 留下一条可吊销的会话记录,让服务端随时能把这把钥匙作废。 坑在哪: 密码明文比对的时...
秒杀系统高并发优化实战(C++ / Drogon):3.3 短信验证码接口:生成 / 限流 / 存储 / 腾讯云直连
注册/登录要不要验证码?要——但验证码接口本身就是个高价值攻击面:它既能当"免费短信轰炸机"打别人的手机,又能被脚本爆破出 6 位验证码。这一篇把验证码接口做完整:生成(CSPRNG)→ 限流(冷却 + 每日上限)→ 存储(Redis)→ 发送(腾讯云直连),每一道都对应一个具体的攻击面。 配套代码:src/service/SmsService.{h,cc}(生成/限流/存储/校验)、src/service/SmsSender.{h,cc}(腾讯云直连)、src/controllers/SmsController.cc、src/service/SmsSender.cc 的 TC3 签名。 一、短信验证码接口:是什么、坑在哪、本质一句话是什么:POST /api/sms/send 给指定手机号生成 6 位验证码,限流后存入 Redis(有效期 + 重发冷却 + 每日上限),再通过腾讯云短信 API 发出去。校验在注册(3.1)/登录(3.2)流程里调 verifyCod...
秒杀系统高并发优化实战(C++ / Drogon):3.4 注册验证码校验 & Token 持久化到 Redis
3.1 把注册流程的"验证码分支"留了空(先查重再哈希再落库,requireSmsOnRegister=true 时走 sms_->verifyCode);3.2 把"登录成功写 Redis 会话"说了。这一篇把这两件事的接缝焊死:验证码怎么校验、Token 怎么持久化、退出怎么吊销,以及它们背后的 key 约定与配置开关。 配套代码:src/service/UserService.cc(registerUser 的 verifyCode 分支、login 的 sessions_->save、logout 的 sessions_->revoke)、src/service/SessionStore.{h,cc}、配置项 requireSmsOnRegister / requireSmsOnLogin。 一、本篇在链路里的位置:是什么、坑在哪、本质一句话是什么:把"验证码校验"接到注册/登录流程里(挡脚本批量注册),把"登录签发的 JWT"的 ...

