Post

PGO-Runner

提供多种常见运行模式的封装

PGO-Runner

在 Go 语言的业务开发中,我们经常需要控制代码的执行方式:是重试?是限频?还是超时控制? 于是我封装了这些常见的“运行模式”。

为什么需要 Runner?

标准库虽然提供了 time.Ticker, context.WithTimeout 等基础原语, 但在实际业务代码中,直接组合这些原语往往会产生冗长的样板代码。 Runner 将这些复杂的控制流(Control Flow)抽象为简单的 API,让业务逻辑更聚焦。 我们开发业务时重点关注“做什么”,然后结合运行环境,可以套一层“运行模式”, 比如想用日志打印来观测某个循环体的数据变化,但打印前5次足以。 又如某个API很容易因为网络不稳定而超时,希望重试3次后才认为真正的失败。

核心功能封装

Runner 主要覆盖了以下三个维度的运行模式:

  1. 频次控制 (Frequency Control)
    • RunMax(n, fn): 一次性或限次执行。常用于初始化逻辑或特定的断言场景,保证某个操作在生命周期内最多只执行 N 次。使用了 atomic 包确保在高并发下的计数准确性。
    • RunEvery(n, fn): 降频执行。例如在高频的某处循环中,如果你不想每一轮都打印日志,可以使用 RunEvery(100, logFn),每 100 次调用才实际触发一次。
  2. 循环与定时 (Loop & Schedule)
    • RunInterval(ctx, interval, fn): 带上下文的定时执行。相比标准的 time.Ticker 循环,它确保了首次立刻执行(通常轮询任务不希望等待第一个周期),并集成了 context.Done() 的退出机制,防止 Goroutine 泄漏。
  3. 容错与稳定性 (Resilience)
    • RunRetry(n, interval, fn): 简单的重试机制。在调用不稳定的下游服务时,通过简单的循环和休眠实现重试策略。
    • RunTimeout(timeout, fn): 超时熔断。将同步函数包装为异步执行,利用 select 监听 time.After 或执行结果,防止某个卡住的操作拖死整个协程。
This post is licensed under CC BY 4.0 by the author.