Vue3 组件契约:props、事件和插槽要可预测

背景 同一个输入组件在不同页面表现不同,常见原因是 props、事件和默认插槽之间没有稳定约定。组件可复用首先依赖可预测性。 核心判断 props 表达输入,事件表达意图,组件不要暗中修改父级对象。 v-model 对应 modelValue 与 update:modelValue,disabled 时阻止交互。 插槽提供必要上下文,避免暴露内部状态对象。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 const props = defineProps<{ modelValue: string; disabled?: boolean }>(); const emit = defineEmits<{ "update:modelValue": [value: string] }>(); 边界与误区 示例的具名元组 emits 写法要求项目编译器支持。类型签名不能替代事件时机、默认值和键盘行为的文档。 验证方式 以受控组件方式测试输入、清空、禁用和父级更新,确认一次操作只触发约定的事件;再验证键盘与屏幕阅读器可访问性。 小结 输入、输出和扩展点显式化,组件内部状态不要通过隐式副作用泄漏。

2026年9月14日 · 1 分钟 · BvBeJ

C++ 缓存行布局:先减少共享写,再谈微优化

背景 两个线程更新不同计数器,逻辑上没有共享数据,却可能争用同一缓存行。伪共享是布局问题,不是原子变量失效。 核心判断 先确认热点来自并发写和缓存一致性开销。 按写入者划分数据,必要时使用每线程计数后聚合。 只有测量支持时才增加对齐或 padding。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 struct alignas(64) Counter { std::atomic<std::uint64_t> value{0}; }; struct Metrics { Counter accepted; Counter rejected; }; 边界与误区 64 字节只是示例假设,不是所有硬件的保证。过度 padding 会增加工作集,读多写少的数据可能因此更慢。 验证方式 固定线程数、CPU 亲和性和操作次数,对比吞吐及缓存相关事件。保留真实业务基准,不用单一空循环决定结构布局。 小结 高频并发写字段要避免伪共享,但任何 padding 都必须由 profile 证明收益。

2026年9月13日 · 1 分钟 · BvBeJ

Rust 借用 API 设计:把生命周期约束藏在正确边界

背景 解析函数返回输入切片通常很自然,但把数据库连接或内部缓存的生命周期暴露出去,会让调用方被迫理解实现细节。 核心判断 只有结果确实借用输入时,才把生命周期关系写进 API。 跨任务、缓存或异步队列的结果通常需要拥有数据。 优先让编译器推断生命周期,不为形式完整添加无意义参数。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 pub fn split_at<'a>(input: &'a str, sep: char) -> Option<(&'a str, &'a str)> { input.split_once(sep) } 边界与误区 示例中返回值确实指向 input;separator 是值,不参与借用关系。一次小复制可能换来更简单的长期接口,不应只追求分配次数。 验证方式 编写结果不能超过输入存活时间的编译失败测试;再测长期保存结果的场景,确认必要的 owned 转换发生在清晰边界。 小结 公共 API 只暴露真实需要的借用关系,避免为了少一次分配泄漏内部生命周期。

2026年9月12日 · 1 分钟 · BvBeJ

Go HTTP 服务优雅关闭:先拒绝新请求,再等待旧请求

背景 发布时立即退出进程,会中断正在处理的请求;无限等待又会超出编排系统给的终止窗口。退出应该有顺序和总预算。 核心判断 先标记不可就绪,并按端点传播延迟安排排空。 收到信号后使用新的带超时 context 调用 Shutdown。 HTTP 排空后再等待后台任务,超时策略和强制关闭必须显式记录。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 signalCtx, stop := signal.NotifyContext( context.Background(), os.Interrupt, syscall.SIGTERM, ) defer stop() <-signalCtx.Done() // ready 是探针读取的 atomic.Bool;摘流量传播等待按部署环境安排。 ready.Store(false) shutdownCtx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() if err := server.Shutdown(shutdownCtx); err != nil { slog.Error("HTTP drain failed", "error", err) _ = server.Close() } // 在总退出预算内,另行停止并等待后台任务。 边界与误区 信号 context 收到 SIGTERM 后已经取消,不能直接拿它执行排空。Shutdown 不负责回收 hijacked/WebSocket 连接;主协程也必须等待关闭流程结束。 验证方式 同时发送短请求、长请求和 WebSocket 再终止服务,检查完成率与退出时长。ListenAndServe 返回 ErrServerClosed 属于正常关闭,不应记成启动失败。 小结 readiness 先摘流量,Shutdown 再等待连接和后台任务,并设置明确上限。

2026年9月11日 · 1 分钟 · BvBeJ

TypeScript 可辨识联合:让状态分支和数据绑定

背景 请求成功时才有 data,失败时才有 error。这种关联如果只靠可选字段表达,调用方很容易忘记判定状态。 核心判断 每个分支使用唯一字面量作为判别字段。 switch 分支内使用对应数据,让编译器完成收窄。 用 never 做穷尽检查,新增状态时迫使消费者同步处理。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 type Result<T> = { status: "loading" } | { status: "ok"; data: T } | { status: "failed"; error: Error }; 边界与误区 可辨识联合只约束编译期代码,不能证明 HTTP 返回的 JSON 符合声明。不要在最后用类型断言绕过穷尽检查。 验证方式 增加一个 cancelled 状态,确认遗漏的消费者编译失败;边界输入单独做运行时校验,测试不合法状态与缺失字段。 小结 为每种状态定义唯一字面量字段,让编译器阻止 loading、error、data 的错误组合。

2026年9月10日 · 1 分钟 · BvBeJ

PostgreSQL 联合索引:左前缀原则不是万能口诀

背景 同一个联合索引有时帮助过滤,有时主要帮助排序。只背左前缀口诀,容易忽略查询实际扫描了多少数据。 核心判断 先列出等值条件、范围条件、ORDER BY 和 LIMIT,再选择列顺序。 检查 EXPLAIN 中的索引条件、过滤行数和排序节点。 同时计算索引对写入、磁盘和维护的成本。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 CREATE INDEX CONCURRENTLY idx_orders_user_status_created ON orders (user_id, status, created_at DESC); EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE user_id = 42 AND status = 'paid'; 边界与误区 索引是否使用以及缺失前导列时是否有收益,要看版本、数据分布和计划。CREATE INDEX CONCURRENTLY 不能放在事务块中,失败后还要检查无效索引。 验证方式 用大租户、小租户和不同状态分别测计划,记录 buffers 与耗时。示例 EXPLAIN ANALYZE 会执行查询,线上需控制成本。 小结 索引列顺序要结合等值过滤、范围条件、排序和实际执行计划决定。

2026年9月9日 · 1 分钟 · BvBeJ

OpenTelemetry 采样策略:让重要请求留下完整证据

背景 只做小比例入口采样,最想排查的错误请求也可能被丢掉。尾部采样可以根据已接收的 trace 内容决策,但需要付出缓存与等待成本。 核心判断 错误、慢请求和基础概率策略分别配置,给普通请求保留代表性样本。 同一 trace 的 spans 必须汇聚到同一尾采样实例。 按请求速率与决策窗口估算内存,监控缓存压力和丢弃情况。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 processors: tail_sampling: decision_wait: 10s num_traces: 50000 policies: - name: errors type: status_code status_code: status_codes: [ERROR] - name: slow type: latency latency: threshold_ms: 1000 - name: baseline type: probabilistic probabilistic: sampling_percentage: 5 边界与误区 示例是 Collector contrib 的处理器片段,需接入 traces pipeline。入口已经丢弃的 spans 无法被尾采样恢复,决策窗口也不能保证迟到 span 完整保留。 验证方式 发送成功、ERROR 状态和高延迟三类 trace,核对实际留存比例。扩容 Collector 时检查 trace 分片和完整性,而不只看导出吞吐。 小结 基础比例采样之外,应优先保留错误、超时和高价值业务请求。

2026年9月8日 · 1 分钟 · BvBeJ

Linux 进程基线:先建立正常画像,再判断异常

背景 线程数从几十变成几百是否异常,取决于请求量和运行阶段。没有正常基线,排障很容易只盯着某个绝对值。 核心判断 按同一进程版本记录 RSS、线程、FD、连接数与请求负载。 区分预热增长、稳定平台和持续线性增长。 采样时记录 PID 与启动时间,进程重启后不要拼接成一条曲线。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 pid=$(pidof app); ps -o pid,ppid,stat,%cpu,%mem,rss,nlwp,etime -p "$pid"; ls "/proc/$pid/fd" | wc -l 边界与误区 pidof 可能返回多个 PID,示例适用于单实例进程;多实例请逐个采样。RSS 增加也可能是缓存增长,不能单凭它认定泄漏。 验证方式 保留空载、稳定负载和峰值三个阶段的快照。停止压测后观察资源是否回到可解释的平台,再决定是否进一步抓堆或分析 FD。 小结 围绕进程树、线程数、文件描述符、RSS 和连接数建立可比较的时间序列基线。

2026年9月7日 · 1 分钟 · BvBeJ

Kubernetes Readiness:就绪探针要回答“现在能接流量吗”

背景 服务进程活着,不等于已经完成缓存预热或可以稳定接流量。把这些状态混在同一个探针里,可能导致依赖故障时不断重启。 核心判断 readiness 只决定是否接收流量,liveness 判断是否需要重启。 启动较慢的服务使用 startupProbe,给初始化单独的时间预算。 检查保持轻量,依赖失效是否摘流量要结合降级能力决定。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 readinessProbe: httpGet: { path: /readyz, port: 8080 } periodSeconds: 5 failureThreshold: 3 边界与误区 配置片段应放在容器字段下。把数据库波动直接绑定 liveness,容易放大故障;把所有外部依赖绑定 readiness,也可能让整个服务同时摘除。 验证方式 分别模拟冷启动、依赖不可用和退出过程,观察探针结果、Service 端点和请求成功率,确认摘流量不会引发重启风暴。 小结 readiness 判断服务是否能接流量,liveness 只判断进程是否需要重启。

2026年9月6日 · 1 分钟 · BvBeJ

Dev Container 一致性:把本地环境差异收敛到配置里

背景 本地编译器与 CI 相差一个版本,就可能出现本地通过、流水线失败。开发容器的价值是把工具链依赖写成可审查的输入。 核心判断 镜像、扩展和服务依赖都记录在配置里,CI 使用相同版本约束。 依赖下载放在创建阶段,调试端口、工作目录和用户权限显式声明。 需要严格复现时锁定镜像 digest,更新版本通过独立变更完成。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 { "image": "mcr.microsoft.com/devcontainers/go:1-1.24-bookworm", "postCreateCommand": "go mod download" } 边界与误区 示例中的版本只是固定版本示意,不是最新版本建议。容器不会自动统一宿主机内核、CPU 架构或文件系统行为。 验证方式 从干净环境重新创建容器,执行测试与构建;同时在实际部署架构验证产物,尤其注意 Raspberry Pi 与开发电脑的架构差异。 小结 开发容器应固定编译器、CLI 和服务依赖,并让 CI 使用同一套版本约束。

2026年9月5日 · 1 分钟 · BvBeJ