专注系统底层与高性能服务开发,持续记录 Go / Rust / C++ / 云原生的一线实践。
从源码细节到线上治理,尽量少空话,多代码。
专注系统底层与高性能服务开发,持续记录 Go / Rust / C++ / 云原生的一线实践。
从源码细节到线上治理,尽量少空话,多代码。
背景 微服务调用里,服务发现经常被当成理所当然的基础设施。但注册中心一旦抖动,调用链就会被放大影响。 实用策略 本地缓存上次可用实例列表 失败时指数退避刷新 查询失败时优先用“最近成功快照” type Resolver interface { Resolve(ctx context.Context, service string) ([]string, error) } type SnapshotCache struct { mu sync.RWMutex data map[string][]string } 总结 服务发现的核心目标是“可用优先”,而不是“每次都拿最新”。 基础组件会失败,容错设计要把失败当常态。
问题不在快,而在失控 很多 io_uring 服务在压测里吞吐漂亮,线上却出现尾延迟飙升。根因通常是提交队列无限推进,完成队列消费跟不上。 背压触发条件 SQ ring 使用率超过 80%。 CQ backlog 持续增长。 业务层处理耗时超过 IO 完成速率。 建议的三层背压 提交层:限制 in-flight 请求上限。 协程层:队列超阈值时暂停新任务调度。 入口层:对上游返回 retry-after 或降级响应。 伪代码 if (inflight > max_inflight || cq_backlog > cq_limit) { pause_accept(); shed_low_priority(); } while (io_uring_peek_cqe(&ring, &cqe) == 0) { handle_cqe(cqe); io_uring_cqe_seen(&ring, cqe); } 关键指标 submit_to_complete_latency cq_backlog inflight shed_count 小结 io_uring 的上限很高,但系统稳定性的上限由背压机制决定。先把“慢下来也不爆炸”做对,再追求“快”。
背景 Tokio 里 select! 和超时很常用,但取消发生在任意 await 点,任务可能停在中间状态。 实践建议 把副作用操作放在不可分割阶段 写操作尽量幂等 关键路径加补偿或重试机制 tokio::select! { _ = shutdown.recv() => { tracing::info!("cancelled"); } res = do_commit_work() => { res?; } } 总结 取消安全本质是状态机设计,不是语法问题。 异步代码能停下来不难,停得干净才难。
典型事故 业务高峰来临,HPA 根据 CPU 拉副本;同时 VPA 建议提升 requests,导致 Pod 频繁重建。结果不是稳定,而是不断抖动。 协同原则 HPA 负责“横向弹性”(副本数)。 VPA 负责“纵向建议”(资源基线)。 两者不要同时直接控制同一 Deployment 的同一资源维度。 实操模式 在线服务:HPA + VPA(recommendation only)。 离线作业:VPA(auto) + 关闭 HPA。 把 VPA 建议周期性写回 Helm values,再经灰度发布生效。 关键参数 HPA 目标指标建议使用自定义业务指标(QPS、队列深度),不要只盯 CPU。 HPA behavior 里设置 scaleDown 稳定窗口,防止“刚扩就缩”。 VPA 设置 minAllowed/maxAllowed,避免极端建议。 观测面板建议 当前副本数与目标副本数差值。 Pod 重建频率与重建原因。 requests 利用率分布(而非平均值)。 小结 HPA 与 VPA 本质上是两个控制器。让它们协同的关键,不是“都开”,而是“职责分离 + 变更节流 + 可观测闭环”。
背景 Rust 接入存量 C++ 代码是很多团队都会走的一步。 难点通常不在 extern "C",而在这些边界问题: 谁创建谁释放 错误如何跨边界传递 线程模型是否一致 基本原则 FFI 边界尽量窄 数据结构扁平、可序列化 所有权规则在接口文档里写死 #[no_mangle] pub extern "C" fn sum(a: i32, b: i32) -> i32 { a + b } extern "C" int32_t sum(int32_t a, int32_t b); 总结 FFI 能带来渐进迁移收益,但边界规范必须比普通模块更严格。 跨语言最怕“默认约定”,最好全部显式化。 边界是系统最脆弱的地方,跨语言边界更是。
背景 前端和 BFF 的协作常见问题: 字段命名不统一 可空语义不一致 线上响应结构和文档不一致 契约化思路 以 schema 为单一事实来源 前后端共享类型生成 关键接口做契约测试 export interface UserProfileDTO { id: string name: string email?: string roles: string[] } export async function fetchUserProfile(id: string): Promise<UserProfileDTO> { return http.get(`/api/users/${id}`) } 总结 契约稳定之后,联调成本会明显下降。 接口变更可追踪,线上兼容风险也更低。 协作效率的上限,通常由契约清晰度决定。
为什么“打开 O3”还不够 大型 C++ 服务的热点跨模块分散,单文件优化难以奏效。LTO 能打通跨 TU 优化,PGO 能把优化预算聚焦在真实热路径。 推荐流程 基线版本:记录 p50/p99、CPU、指令数、缓存 miss。 LTO 版本:先验证链接时间与二进制体积变化。 PGO 训练:必须使用“接近线上”的请求分布。 组合验证:LTO + PGO 与基线做 A/B。 CMake 关键配置示例 # LTO set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) # PGO 两阶段 # 1) -fprofile-generate # 2) -fprofile-use -fprofile-correction 最常见的误区 用单一压测脚本训练 PGO,导致优化偏科。 只看吞吐,不看长尾延迟和抖动。 忽略符号变化对排障工具链(perf、addr2line)的影响。 回归防线 增加“训练集漂移”检测:训练流量和线上流量偏差超阈值就拒绝发布。 将“优化收益”拆解到函数级,防止偶然抖动误判。 为 PGO 单独维护回退开关和构建产物。 小结 LTO/PGO 是生产力工具,不是玄学加速按钮。只有接入真实流量画像和回归防线,优化收益才可持续。
背景 在微服务里,重试是常态;没有幂等,重试就会制造脏数据。 常见重复来源: 客户端重复点击 网关超时后自动重试 消息队列重复投递 一种常见实现 以幂等 key 做唯一约束,先查后写或直接 UPSERT。 type IdempotencyRecord struct { Key string Status string ResultRef string } func (s *OrderService) CreateOrder(ctx context.Context, key string, req *CreateReq) (*Order, error) { if rec, ok := s.repo.FindByKey(ctx, key); ok { return s.repo.FindOrder(ctx, rec.ResultRef) } order, err := s.repo.CreateOrderWithKey(ctx, key, req) if err != nil { return nil, err } return order, nil } 总结 幂等设计本质是在失败重试下维持业务语义稳定。 关键是“唯一键 + 状态机 + 可重放结果”。 高可用系统默认会重试,幂等就是重试的安全带。
背景 同样是自动伸缩,HPA、VPA、KEDA 解决的问题并不一样。 选择思路 请求型 Web 服务:优先 HPA 资源画像长期不准:引入 VPA 做建议或自动调参 事件驱动消费:优先 KEDA 常见组合 HPA + Cluster Autoscaler:最常见 HPA + KEDA:API + 消费任务混合系统 VPA 先建议模式观察,再决定是否自动 总结 不要为“自动化程度更高”盲目上更多组件。 先把指标质量做好,再谈伸缩策略。 伸缩策略的上限,取决于指标体系的下限。
背景 不是所有性能优化都要上自研内存池。很多时候,std::pmr 已经能解决不少问题。 一个实用场景 请求处理阶段会构建很多临时字符串和容器,生命周期一致,适合放在同一块内存资源里。 #include <memory_resource> #include <string> #include <vector> void handleRequest() { std::byte buffer[4096]; std::pmr::monotonic_buffer_resource pool(buffer, sizeof(buffer)); std::pmr::vector<std::pmr::string> fields{&pool}; fields.emplace_back("user", &pool); fields.emplace_back("email", &pool); } 总结 pmr 的价值在于“低侵入地控制分配策略”。 在临时对象密集场景里,收益通常比预想更明显。 能用标准库解决的问题,优先别把复杂度推到自研。