这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《Python高级编程》9.3 PyO3 与 Rust 扩展
讲清 PyO3 0.29.3 用 Rust 写扩展模块的架构:声明式模块、Bound<'py, T> 如何把 GIL 生命周期编进类型、detach 释放 GIL、abi3 与 maturin 分发,并用一个实测的 cffi 最小扩展做对照取舍。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
讲清 PyO3 0.29.3 用 Rust 写扩展模块的架构:声明式模块、Bound<'py, T> 如何把 GIL 生命周期编进类型、detach 释放 GIL、abi3 与 maturin 分发,并用一个实测的 cffi 最小扩展做对照取舍。
实测 Cython 3.3.0 把 .pyx 编译成 C 的完整过程:cdef 类型声明的真实加速比、cython -a 的 C-API 调用密度注释、clang -O2 把循环闭式化的反直觉现象,以及 NumPy 内存视图与 nogil 实测。
从 CPython 用 dlopen 加载符号的机制讲起,实测 ctypes 的 errno 传递、内存所有权与 Structure 布局,再对比 cffi 的 ABI/API 两种模式,并给出 ctypes.CDLL、PyDLL 与 cffi 四条调用路径的 GIL 释放与单次调用开销实测数据。
新版本不该一次性推给所有用户。本节讲灰度发布的三种流量切分(按比例/按用户/按头)并用纯 Python 实测稳定哈希分流与自动准入判定,讲不可变镜像 tag 回滚与数据库前向兼容(expand-contract)约束,最后给出一份可执行的故障演练清单与复盘模板。
单机日志文件在多实例部署下形同虚设。本节用 structlog 26.1.0 真跑「采集→存储→查询→告警」链路,把标准库与第三方库日志也统一成 JSON,用级别与采样控制成本,让日志带上 trace_id 可一键跳转追踪,并写出可降噪的告警规则。
日志知道发生了什么、指标知道系统健不健康,追踪回答「一次请求慢在哪一段」。本节用 opentelemetry-api/sdk 1.45.1 真跑 TracerProvider、嵌套 span 的父子关系与耗时、手动埋点与装饰器、W3C traceparent 跨服务传播,并贴出 FastAPI 0.143.0 内建埋点导出的真实 SERVER span。
玩家掉线后必须能秒回游戏,但又不能凭一个账号名就免密登入。本节给 aegis 设计一张重连票据:用 HMAC-SHA256 签发、绑定账号与连接序号、带过期时间、且只能使用一次。我们用标准库把它从零写出来,并用测试逐一证明正常校验、签名被篡改、过期、以及同一票据二次使用这四种路径的行为。
长连接最大的敌人不是掉线,而是你以为它还活着。本节给 aegis 写一个真实的 WebSocket 服务端:一条读超时、一个看门狗、一路可选的服务端 ping,然后用真起端口、真连的 L2 测试验证「不发心跳的客户端会被主动关闭并清理会话」,最后报告本机实测的超时精度与 1000 个空连接的心跳开销。
浏览器与小程序只能发起 HTTP 请求,MMO 网关的长连接入口因此多半是 WebSocket。本节不引任何第三方库,用标准库手写 RFC 6455 里网关真正需要的一小块:从 Sec-WebSocket-Key 拼 GUID、SHA-1、Base64 回 101,到帧头的 FIN、opcode、MASK 与 126/127 扩展长度,再到客户端必须掩码的逐字节 XOR 与分片重组。
本节用一个固定种子的确定性弱网模拟器,把 5.2 的 KCP 放在 0%/1%/5%/20% 四档丢包下实测:记录每条消息的端到端延迟 P50/P99 与冗余发送率,并与 5.1 的单流 TCP 式基线正面对比。同一丢包率下,KCP 靠快速重传把 P99 明显压低,代价是 20% 丢包时约 1.5 倍的冗余发包。
本节从零实现一个教学版 KCP:24 字节报文头、PUSH/ACK/WASK 三种命令、基于 SRTT 与 RTTVAR 的 Jacobson RTO 估计、una 累积确认的滑动窗口、以及靠重复 ACK 触发的快速重传。接口只有 Send/Recv/Input/Update 四个,五个测试覆盖正常收发、丢包重传、乱序重组、重复去重与 RTO 收敛。
TCP 在实时对战里有三个硬伤:队头阻塞让一个丢包卡住后面所有消息,重传策略不区分消息重要性,拥塞控制在弱网下退让过猛。本节写一个确定性模拟器,在同一丢包率与 RTT 下对比单条有序流与多条独立通道的延迟分布,把队头阻塞的代价变成可以复现的数字。
同一条移动通知,用 JSON、手写扁平化协议与手写 varint 二进制分别编解码,本机跑出 ns/op、B/op 与 allocs/op。JSON 编解码要几百纳秒和几次堆分配,手写二进制只要十几到二十几纳秒、零到一次分配。本节给出真实数字表格与选型判据,并说明 MessagePack 与 Protobuf 为什么不在本节实测。
编解码一半时间花在拷贝上。本节对照 Go 与 Java 两种零拷贝:Go 侧用切片视图在同一底层数组上切出 payload,改一个字节即可证明不复制;Java 侧用 JDK 自带 ByteBuffer 的 slice/duplicate/asReadOnlyBuffer,并在 Temurin 21 容器里真跑。最后给出切片够用与必须池化的判定。
二进制编解码是 MMO 服务端最底层的性能开关。本节从零写一个 internal/codec 包:Writer 按协议顺序压入 uint8/16/32/64、varint、字符串与字节块,Reader 逐字段读取并对每次越界返回错误而不是 panic;再用它编解码真实的 MoveNotify 结构体,test 覆盖往返一致、varint 边界与越界读取。
service 抛出的领域错误怎么变成正确的 HTTP 状态码?本节把错误映射收敛到一处:定义领域错误集、用 errors.Is/As 分支把 ErrNotFound 映射成 404、把校验错误映射成 422,并给出统一的 JSON 错误响应结构。同时实现带字段的输入校验,让客户端知道到底是哪个字段不合法,最后强调不要把内部错误细节泄露给外部。
配置有了,谁来把它组装成运行中的对象?本节讲 TaskAPI 的分层:handler 只做协议转换、service 承载业务规则、store 负责持久化,三者用接口解耦。然后演示 Go 惯用的手工依赖注入——在 main 这个 composition root 里把所有依赖 new 出来、逐层注入,不引入任何 DI 框架,并用 fake store 证明层与层之间可替换。
把端口、超时、数据库地址写死在代码里,是项目长大的第一道坎。本节讲清配置的三个来源——命令行 flag、环境变量、JSON 配置文件——各自的读取方式与适用场景,给出「flag > env > 文件 > 默认」的合并优先级实现,用 flag.Visit 区分「显式设置」与「默认值」,并在启动时做 fail-fast 校验。
相同参数不等于同一次业务。给同一客户发送相同正文的第二次合法通知应允许执行;重试同一通知则应复用原操作键。本进程锁串行包住查缓存、执行和存结果,防止同进程并发重复。snapshot/restore 保留已记录结果;函数异常不缓存,失败前是否已经产生副作用必须由业务确认。
asyncio.timeout 与 TaskGroup 通过取消协作任务实施超时。工具在 CancelledError 后清理资源并重新抛出,不能吞掉取消继续运行。同步线程、阻塞扩展和外部副作用不会因此被强制终止。to_thread 只转移阻塞工作;取消 await 不停止已经执行的线程。强终止需要独立进程与相应资源生命周期设计。