专注系统底层与高性能服务开发,持续记录 Go / Rust / C++ / 云原生的一线实践。
从源码细节到线上治理,尽量少空话,多代码。
专注系统底层与高性能服务开发,持续记录 Go / Rust / C++ / 云原生的一线实践。
从源码细节到线上治理,尽量少空话,多代码。
背景 C++ 协程常见 bug 之一是对象在挂起后已经销毁,但恢复时仍被访问。 典型风险 捕获局部引用并跨 suspend 使用 返回协程句柄后调用方提前释放上下文 Task<void> foo() { std::string buf = "hello"; co_await suspend_point(); use(buf); // 若生命周期判断错,这里会出问题 } 总结 协程代码要像异步状态机一样审生命周期,别按同步函数直觉来读。 控制流变了,生命周期审计方式也必须跟着变。
背景 Monorepo 常见痛点是:改了一个子目录,多个镜像都触发重建。 改进方向 精细化 .dockerignore 子项目独立 context 共享基础层分离 COPY services/api/go.mod services/api/go.sum ./ RUN go mod download COPY services/api/ ./ RUN go build -o app ./cmd/api 总结 缓存命中率是 CI 成本的核心变量,Monorepo 必须做分层构建设计。 构建系统的复杂度,迟早会和代码规模一起增长。
关键事实 Future 在 .await 点可能被取消。若状态更新分布在多个 await 之间,就可能出现“写了一半”的业务状态。 设计原则 把副作用集中在单一提交点。 在可取消区间只做纯计算或幂等准备。 对外部系统写入使用幂等键。 反例与修正 // 反例:先扣库存再写订单,两个 await 中间可被取消 reserve_stock().await?; create_order().await?; // 修正:准备阶段无副作用,最后一次性提交 let plan = build_plan().await?; commit(plan).await?; 工程策略 为关键流程增加“中断注入测试”。 对每个 await 标注取消后的状态语义。 引入补偿任务清理孤儿状态。 小结 异步取消是默认行为,不是异常路径。把 cancellation safety 当作接口契约的一部分,才能避免线上出现“偶发且不可复现”的脏状态。
典型症状 单连接吞吐很高,但进程 RSS 持续上涨。 p99 抖动明显,GC 时间占比异常。 下游稍慢就触发级联超时。 流控设计要点 应用层窗口:限制每个 stream 的未确认消息数。 连接层隔离:大流量 stream 与普通 RPC 分离连接。 消费层背压:处理队列满时暂停读或降级。 服务端模式 type StreamState struct { inflight int64 limit int64 } func (s *StreamState) AllowRecv() bool { return atomic.LoadInt64(&s.inflight) < s.limit } 参数调优建议 MaxRecvMsgSize 不要无限放大,优先拆包。 对大对象优先走分块传输。 结合业务 ACK 做“应用级信用”控制。 观测面 每 stream inflight 数。 解码耗时与业务处理耗时拆分。 内存分配热点(pprof alloc_space)。 小结 Streaming 的本质是长期会话。想要稳,必须让发送速率服从消费能力,而不是盲目追求“尽快塞满管道”。
背景 扩缩容、实例重启、网络波动都会触发 rebalance。处理不好就会出现消费停顿、重复处理和延迟暴涨。 实践要点 处理逻辑幂等化 offset 提交时机明确 重平衡回调里做好 flush for msg := range claim.Messages() { if err := handle(msg); err == nil { session.MarkMessage(msg, "") } } 总结 消费者稳定性的上限,取决于你对 rebalance 的设计,而不是对“正常流量”的设计。 消息系统里,异常路径才是主路径。
背景 async_trait 大幅提升了工程可读性,但在高频路径里也可能引入额外分配和动态分发开销。 选择建议 热路径优先泛型静态分发 插件式扩展再考虑 trait object 先 benchmark 再下结论 pub trait Storage { fn get<'a>(&'a self, key: &'a str) -> Pin<Box<dyn Future<Output = Option<String>> + Send + 'a>>; } 总结 抽象不是免费的,但可维护性也有价值,关键是按热点分层。 架构取舍从来不是二选一,而是按场景分配复杂度。
目标先定清楚 供应链安全的核心不是“发现漏洞”,而是回答三个问题: 产物是谁构建的? 构建过程是否可复现、可验证? 已知风险是否能阻断上线? 最小可行链路 构建时生成 SBOM(CycloneDX/SPDX)。 对镜像和 SBOM 进行签名(cosign)。 在部署前做签名与策略校验(admission policy)。 流水线分层 Build:固定基础镜像 digest,避免 tag 漂移。 Attest:记录构建来源、commit、runner 信息。 Verify:发布阶段验证签名、来源、漏洞阈值。 策略示例 rules: - require_signature: true - require_sbom: true - max_critical_vulns: 0 - trusted_builders: - ci-prod-runner 常见失败点 只签镜像不签 SBOM,证据链断裂。 允许“人工例外”但不留审计记录。 漏洞阈值过严导致全线绕过,最终失去治理。 小结 把“生成证据、验证证据、阻断风险”串成默认流水线,供应链安全才会从一次性项目变成长期能力。
背景 很多集群在节点升级、手动驱逐时出现服务抖动,根因常常是没配或错配 PDB。 基本配置 apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: api-pdb spec: minAvailable: 2 selector: matchLabels: app: api 总结 PDB 不是“可选增强”,而是生产集群进行维护操作的安全护栏。 维护窗口稳定与否,很多时候取决于这几行 YAML。
背景 很多后台系统首屏不慢,但页面切换时常有明显等待。通常是路由组件和数据都在点击后才加载。 实用做法 鼠标悬停时预取路由 chunk 关键列表页预取首屏数据 低网速场景要可降级关闭 const UserPage = () => import('@/pages/UserPage.vue') function prefetchUserPage() { void UserPage() } 总结 预取策略要适度,目标是减少等待感,不是盲目预加载所有页面。 用户感知速度,很多时候来自“提前一步”。
背景 很多 allocator 优化在 micro benchmark 里很好看,上线却收益一般。原因是评测维度不完整。 建议指标 p50/p95/p99 分配耗时 长时间运行碎片率 多线程争用下吞吐波动 auto begin = std::chrono::steady_clock::now(); void* p = alloc.allocate(256); alloc.deallocate(p, 256); auto end = std::chrono::steady_clock::now(); 总结 评测方法比结果数值更重要,先保证实验可信,再比较方案优劣。 性能数据要能解释真实场景,才有决策价值。