bootstrap

package module
v0.7.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 10, 2026 License: MIT Imports: 13 Imported by: 0

README

bootstrap

应用生命周期引导——统一骨架/资源注册/优雅关停/健康检查

🌐 项目主页:https://oekit.github.io/ · 名称由来:Open Easy Kit(开源 · 易用 · 工具集合) 🤖 AI Agent:本仓规范与硬规则见 AGENTS.md;机器可读索引见 llms.txt(接口签名以源码为准)。

安装

go get github.com/oekit/bootstrap@v0.7.6

这个仓库做什么

bootstrap 是应用生命周期引导:New(With*...) + Run(fn) 提供「初始化 → 运行 → 优雅关停」的统一骨架——资源注册、goroutine 托管、关停顺序、超时兜底全部内建; App.Ready/ReadyChan/IsReady 就绪门配合 bootstrap/nacos 实现「server 就绪后才注册」。

快速使用

import (
    "github.com/oekit/bootstrap"
    "github.com/oekit/hertz"
)

b, _ := bootstrap.New(
    bootstrap.WithServiceName("my-service"),
    bootstrap.WithShutdownTimeout(10*time.Second),
)
_ = b.Run(func(app *bootstrap.App) error {
    h := hertzserver.New(...)
    hertz.RunServer(app, "my-service", h,
        hertz.WithOnReady(func(ctx context.Context) { app.Ready() })) // server 接入生命周期 + 就绪门
    app.AddResource("db", func(ctx context.Context) error { return dbPool.Close() })
    app.GoBackground("worker", workerLoop)       // 托管后台 goroutine(panic 兜底)

    <-app.Context().Done() // ★ 长驻服务必须阻塞:返回即视为服务结束
    return nil
})

BeforeServe:引擎启动前钩子

WithBeforeServe(fn func() error) 在组件初始化后、userFn 之前按注册顺序执行; 任一返回 error 则中止启动。用于必须发生在引擎 serve 之前的准备:

  • 零断点重启的 listener fd 收养(SCM_RIGHTS → restart.Adopt)
  • pid 文件交接
  • 启动前校验
b, _ := bootstrap.New(
    bootstrap.WithServiceName("x"),
    bootstrap.WithBeforeServe(func() error {
        ln, err := restart.Adopt(fds) // 从环境变量收养
        if err != nil { return err }
        myListener = ln
        return nil
    }),
)

bootstrap 不接管 listener 创建/收养;自备 listener 时也可不经本钩子, 直接 server.New(server.WithListener(ln), ...)。

attach 模式

bootstrap.NewApp(mgr *lifecycle.Manager)(github.com/oekit/core/lifecycle)是 attach 模式入口:只把生命周期四阶段挂到调用方的 lifecycle.Manager 上, 不接管信号与 server——不调用 signal.Notify、不调用 h.Spin,SIGINT/SIGTERM 与服务启停由宿主负责。适合宿主已有 Spin/OnShutdown 生命周期(如 hertz), 只想补上「ctx 取消 + 资源关闭 + 后台任务 drain」的场景:

teardown := lifecycle.New(30 * time.Second)
app := bootstrap.NewApp(teardown)
app.GoBackground("outbox", outbox.Run)
app.AddResource("redis", func(ctx context.Context) error { return rdb.Close() })
h.OnShutdown = append(h.OnShutdown, func(ctx context.Context) {
    teardown.Shutdown(ctx)
})
h.Spin()

四阶段顺序语义(job 名 app-lifecycle:{cancel,drain,close,wait}):

阶段 做什么
cancel 取消 app.Context(),通知 GoBackground 退出
drain 等待 Acquire 中的在途请求(shutdown.Manager.Drain)
close 逆序执行 app.AddResource 注册的 closer
wait 等待 app.GoBackground goroutine 退出(超时可配)

lifecycle.Manager 按注册顺序执行 job(注册顺序 = 执行顺序):caller 在 NewApp 之前 mgr.Add 的任务先于四阶段执行;在 NewApp 之后 mgr.Add 的任务排到四阶段之后(错过 close 窗口,应用资源请改用 AddResource/GoBackground)。

健康检查

import "github.com/oekit/bootstrap/healthz"

healthz.RegisterCheck("db", pingDB)   // 多 check 并发探测、总超时、panic 隔离
code, body := healthz.Handle(ctx)      // 全通过 200,任一失败 503 + 详情

// Hertz 服务无需手写 handler:github.com/oekit/hertz 的 health 子包
// 提供 RegisterHealthRoutes(h),内部消费本包 Handle

语义要点

  • app.AddResource(name, closer):关停时按注册逆序释放
  • app.GoBackground(name, fn):托管后台 goroutine(panic 被 recover)
  • 时间关系(K8s):发现延迟 < preStop < server 最大延迟 < ExitWaitTime < terminationGracePeriod

边界

  • Hertz/Kitex 的 RunServer 接入本骨架;NNS 注册用 nnsc.RunWithLifecycle

矩阵总览(24 活跃模块,自动生成)

模块 内容 矩阵内依赖
pkg 微工具集 28 合 1(errors/bloomx/contextx/ctxscope/container/stringx/slicex/enumx/convert/encoding/jsonrepair/httpjson/mathx/pathx/pagination/ptrx/secretx/shellx/timex/cache/bufiox/strmap/singleflighttest/urlguard/webhook/cli/pack/procx;v0.10.0 起 uidgen/validator 拆至 kitx);errors(v0.12.0 起)New/As/Is/Unwrap 与 stdlib 同型透传——下游哨兵/As 判定单 import 即可,类型同一性红线可完整执行;cache(v0.4.0 起)含缓存接口 CacheAside + BatchGetter 批量能力(MGet)+ LocalCache/SingleFlightCache + cachetest 契约(Run 真实时间 + RunWithClock 假时钟确定性 TTL 边界)+ Checker;LocalCache 时钟可注入(LocalConfig.Now——TTL/NullTTL 全路径确定性测试,v0.13.0 起);v0.16.0 起 LocalCache 可选形状——NoBackgroundSweep 关闭后台清扫 goroutine(嵌入式/短命进程,公开 Sweep 手动清扫口)、CloneOnRead 读命中返回副本(调用方可改写不污染缓存)、EvictPolicy 可选 LRU(默认 FIFO 不变)、GetEx 区分未命中与值为 nil(新增能力以可选接口 Sweeper/HitGetter 外置,不破坏其它后端);v0.23.0 起补 Peek 非删除式读(可选接口 PeekGetter 外置——读到过期条目只算不命中、不删除它,stale-if-error 兜底需要那份过期真值留在表内;SingleFlightCache 包装 LocalCache 时曾因前置检查走 Get 惰性删除而静默废掉 StaleIfError,#72);container(v0.9.0 起,收编原 collectionx)泛型容器 Set/Ring/Queue/SafeMap + Map(sync.Map 背书)/Slice(并发安全);mathx/jsonrepair(v0.11.0)Unstable 单边向上抖动 Upward / 宽松解析 UnmarshalLenient / 抽对象与抽数组对称原语 ExtractObject·ExtractArray(v0.18.0 起——多段数组与散文含 '{' 的数组文本不再整段作废,取首个口径与对象面一致);timex/stringx(v0.7.0 起)时间段边界 BeginningOf/EndOf 七对(分/时/日/周/月/季/年,周起始显式参数)+ trie 多模式替换器 Replacer(最长匹配)/ NewMask 敏感词遮罩;stringx Masker(v0.16.0 起)正则脱敏——按形态分类打码(形态标签进掩码供审计读)+ 保留首尾各 ≤4 字符定位线索且不可复原(保留位封顶不随长度变化),规则表由调用方完全提供;singleflighttest(v0.8.0 起)singleflight 共享契约测试套件(internal 与 concurrency 双侧引用);urlguard(v0.15.0 起)出站 SSRF 防护原语——CheckURL 字面量校验(私网/loopback/链路本地含云元数据 169.254.169.254/CGNAT/ULA/组播保留、.internal·.local·localhost 域名、scheme 白名单、IPv4-mapped IPv6 归一)+ DialFunc 拨号层预解析校验与实际 RemoteAddr 端点核对闭合 TOCTOU(Happy-Eyeballs 覆盖),构造态豁免 WithAllowPrivate/WithAllowCIDRs/WithAllowDomains,与 httpx WithDialFunc 一行组合(httpx#11);cli(v0.16.0 起)框架无关可单测的子命令分发原语——App/Command + 退出码 0/1/2 写死在包里、usage 按 Summary 自动生成、不读 os.Args 不调 os.Exit(输出流全由调用方给);pack(v0.16.0 起)迁移包容器原语——zip + manifest(格式版本/条数/每文件 SHA-256)+ JSONL 流式读写 + Inspect 体检一次列全问题并区分包损坏与版本不认识 —
concurrency 并发与流控原语(async/gopool/group/singleflight/progress/syncx/ratelimit/retry)+ window 滑动窗口 / breaker 自适应熔断(规范哨兵 ErrCircuitOpen)/ shedding CPU 自适应降载 / batch 攒批执行器(满条数/满字节/窗口去重)/ timewheel 单级时间轮;v0.5.0 起 retry 指数退避+抖动(MaxDelay 封顶/溢出饱和)、loop 周期原语(忙时丢 tick/单轮 panic 收编/FirstDelay 可打断)、async labeled RunSafe(panic 按任务标签上报)、syncx.FanInErr 逐槽错误(panic 槽显式标记)、KeyedLimiter 增强(容量护栏/RetryAfter/速率热更)并接入 pkg/singleflighttest 契约测试;v0.8.0 起 singleflight DoShared 折叠可观测(Result.Waiters)、async panic 统一收编 typed *PanicError(errors.As 可辨、携带栈)+ 实例级 Runner panic 路由、retry DelayHint(服务端 Retry-After 建议优先)+ JitterRatio 比例抖动 + retry/httpx Retryable 可重试性判定;v0.9.0 起 breaker 显式三态状态机 closed/open/half-open——冷却(WithProbeTimeout 缺省 1m)期满首个放行即半开探测、探测成功关闸清窗/失败重开、失联超时自动回收 + AbandonGen 显式放弃,GenBreaker 代绑定(AllowGen/SuccessGen/FailureGen/AbandonGen:代仅在探测放行自增、陈旧代回报整体作废、开放期失败不后移冷却);v0.10.0 起 WithCooldown 独立业务冷却期(缺省回落 probeTimeout)+ WithConsecutiveFailures 连续失败计数触发(与速率型并联,补低 QPS 信号,对齐 Envoy consecutive_5xx);v0.11.0 起 ratelimit Lockout 防爆破锁(fail2ban 形态:滑窗失败计数→阈值锁定拒绝 LockDuration→成功 Reset 清零/到期自解,CheckLocked 返回剩余时长);v0.12.0 起 retry/httpx RetryableWithHeaders(403 + Retry-After/X-RateLimit-Remaining 头感知可重试判定,HeaderSource 零依赖抽象)、syncx FanInLimit 受限并发扇出(固定 worker 防下游过载)+ FanInLimitCtx(v0.14.0,ctx 感知:ctx 已取消未启动项 fn 一次不调、剩余槽快速排空 Canceled、在飞项照常落槽)、ratelimit WithTokenClock 时钟注入;v0.13.0 起 breaker 熔断期收敛到探测级(forcePass 强制探活 + 冷却期满半开探测,其余拒绝——SRE 公式退役为 closed→open 触发器,WithConsecutiveFailures 触发后流量真停)+ State()/State 枚举可观测出口;v0.16.0 起 ratelimit NewKeyedLimiterByFunc[K]——factory func(K) Limiter 按 key 定配额(一个管理器按接口维度分档位,NewKeyedLimiterOf 零破坏随迁;未登记 key 拿 factory 兜底档的坑已在 doc 点名,#31);v0.17.0 起 KeyedLimiter.Create 护栏内原子登记(表满先回收空闲与 Allow 同路、仍满拒绝计 OverCapHits、已存在不覆盖——热更走 Replace;解决 ByFunc factory 库锁内调用致消费方锁序倒置,#33);v0.18.0 起 retry.DoValue 泛型伴生——带返回值的重试(DoValue[T](ctx, cfg, fn func(attempt int) (T, error));Do 改为委托给 DoValue——退避序列/抖动/MaxDelay 封顶/DelayHint 优先/Retryable 门控/ctx 取消只有一份实现,杜绝「同一个仓里两份实现」;失败返回零值与最后一次错误) pkg
logx 统一日志门面(多后端可替换 + caller 归属;Sink 已迁嵌套模块 logx/sink,根包纯 stdlib);WithFilter 脱敏/丢弃包装(FilterKey 命中键替换 ***/FilterFunc 丢整条,子 logger 保持规则)、Slogger/SlogHandler 正向桥(三方 slog 直接消费 logx 生态)、TraceHandler 任意 slog.Handler 自动注入 trace_id、NewAuditLogger 审计留痕(级别锁 Info,不受 LOG_LEVEL 降级)、LevelEnabler 热路径提前判级、ParseLogLevel 返回 (Level, error) + ParseLogLevelOr 配错退默认(v0.7.0);优雅关停正身在 core/lifecycle.Manager(logx/shutdown 仅余 Deprecated 别名);桥接子包 hlog/hertzlog/kitexlog 为嵌套模块按需引入 core
core 应用基建件——governance 治理 schema、config 配置加载(LoadBootstrap/LoadDotEnv/Get* 系、default/env/range/options tag 校验、OverlaySource 多源叠加热更、ApplyEnvOverlay env 优先 12-factor 形态)、transport 协议无关传输上下文(Transporter/Header/Server·Client ctx 存取,仅标准库)、chain 中间件链(Handler/Middleware/Chain/Recovery/Timeout,与 nns-endpoint.Endpoint 同形)、lifecycle 契约 + Manager 优雅关停(自 logx/shutdown 迁入,日志 *slog.Logger——core 不再依赖 logx)、network IP 工具、version;v0.9.0 起 config FieldSet 显式提供集合(yaml 显式 port:0 不再被 default/env 覆盖——LoadBootstrap 默认接线,免全量指针化)+ require tag(必须 yaml/env 提供,缺失列全并附 env 提示)+ v0.9.1 修 yaml:,inline 字段在 FieldSet 下漏标记(inline 叶子显式零值曾被 default 覆盖);v0.9.2 env 卫生改纯静态(仅格式/重名,移除依赖进程环境的拼写冒烟检查——误报 PATH/HOME、漏无人设置的真拼错,core#8),命名一致性走 LintEnvNaming 诊断 / WithEnvNamingLint 可选 fail-closed / ignorederived="true" 豁免;v0.10.0 起 config 严格模式——LoadBootstrap[T](envFlag string, opts ...LoadOption) 传 config.WithStrict() 开启:未知键拒载(不再静默忽略),错误带嵌套路径 + 该层合法键清单 + 近似拼写建议(UnknownKeyError);默认行为不变,严格模式显式开启;v0.11.0 起 config 加载入口按来源拆开——LoadFile(任意路径:CLI -config 路径、容器内固定挂载位)+ Unmarshal(字节源:API 下发 / 模板渲染 / 测试夹具),与 LoadBootstrap(etc/ 约定)共享同一条解码线,因此严格模式诊断面(嵌套路径 / 合法键清单 / 拼写建议)与来源无关;LoadBootstrap 行为与错误文案逐字不变 —
kitx 标识与校验——uidgen(Snowflake 全局单例+Extract 元数据、手写 RFC 4122 v4 UUID 零第三方依赖、RandomHex/RandomString 拒绝采样随机串)+ validator(go-playground/validator v10 接口化门面:struct tag 校验/自定义规则注册/ValidationError Unwrap 为 BizError 400 对接 middleware.Fail)+ hash(v0.2.0 起非密码学哈希助手 Md5Hex/Md5Bytes——缓存键/幂等键/短唯一 code,格式稳定承诺,pkg 2026-09-15 治理移除弱哈希后 kitx 侧收编 pkg#10),自 pkg v0.10.0 拆出 logx、pkg
metrics Prometheus 门面 + 弹性指标族 core
tracing OpenTelemetry 追踪 + trace_id 真相源(v0.3.0 起 OTLP 双协议导出:gRPC 缺省 / HTTP,Config.Protocol + Headers,切换协议不丢传播器/指标/旧 provider 关闭) logx
registry 注册发现核心(provider 无关 SPI:Registrar/Discoverer/Subscriber/ConfigStore + 实例模型 + netutil)+ sdbridge + registrymocks——根仓零第三方依赖;Nacos 实现与 Nacos 配置源已拆成嵌套模块(v0.5.0 起:registry/nacosimpl、registry/nacos——nacos-sdk-go 重依赖隔离在此,实测单独引入即拖 63 个第三方模块,不隔离时只要 SPI 接口的消费方也会被连带拖入) core
data 数据访问门面——复刻 ekit data/ 能力面:cache/rediscache/crud/distlock/gormx/redisx/timertask/cachetest 八子包 re-export(类型同一,实现住 pkg 与各子仓)+ cached 缓存读写编排(写后失效/事务提交后失效/查询缓存版本号失效/GetByIDs 批量点读 MGet 快路径/bloom 防穿透)+ twolevel 两级缓存与跨实例失效广播(v0.7.0 起本仓唯二非 re-export 实现);v0.15.0 起壳跟随源仓演进——cache 壳补 LocalCache 可选形状(EvictPolicy/Sweeper/HitGetter)、cachetest 壳补 5 个契约套件入口(套件 8 → 13 场景)、v0.18.0 起 cache 壳补 PeekGetter 别名 + cachetest 壳补 2 个 stale-if-error 契约入口(套件 13 → 15 场景)、timertask 壳的 Redis 形态随源仓 v0.2.0 迁至嵌套模块 timertask/redis(壳不再转发 RedisBackend/ShardManager,Redis 形态直接依赖该嵌套模块);all/ 聚合模块已废弃 retract concurrency、gormx、logx、pkg、redisx、timertask
gormx SQL/GORM 全家——连接池增强、框架无关 CRUD Service、仓储(原 oekit/gorm,v0.3.0 更名) logx、pkg
redisx Redis 全家——客户端封装(根包)、分布式缓存 cache/、分布式锁 distlock(Locker 同形可互换;Renew 手动续期(false=锁丢失,不进 Locker 接口保 etcdx 互换)+ WithAutoRenew 覆盖 TryLock 自动续期(v0.8.0 feat!:此前组合不续期);Semaphore ZSET 槽位信号量(v0.8.0:Acquire 同 identifier 幂等刷新 + Lua 内原子清过期槽崩溃自愈、Renew 仅未过期槽可续、Cleanup 返条数、无 Redis 降级 fail-fast))、分布式限流 ratelimit(Lua 三态 + 故障本地兜底);v0.9.0 起 distlock Lockout 分布式防爆破锁(Redis 侧 fail2ban:滑窗失败计数→阈值锁定 Penalty→Reset 清零,与 concurrency/ratelimit.Lockout 进程内孪生——多实例共享计数是存在理由;惩罚只按首次触发计算、nil client fail-fast 不降级,#6) concurrency、logx、pkg
etcdx etcd 全家——客户端封装(根包:生产默认值/Health 探活/Close(ctx) 幂等)、registry 注册发现实现(SPI 对接 registry/nacos 链路:Lease KeepAlive 注册 + 丢约指数退避自动重注册、prefix Watch 订阅推送、ConfigStore KV 配置监听)、distlock 分布式锁(Session+concurrency.Mutex 公平排队,接口与 redisx/distlock.Locker 同形可互换,可选无 etcd 降级放行) concurrency、logx、registry
httpx HTTP 客户端 + 负载均衡——服务发现/熔断(规范哨兵 ErrCircuitOpen)/重试/指标/OTel 透传;loadbalance Selector 形态(RoundRobin/Random/Weighted/一致性哈希权重感知虚拟副本/p2c 自适应——EWMA 经 DoneFunc 回写建立、Err 计 1s 惩罚;WithFilter 按元数据过滤;v0.4.0 起 Balancer 全家族删除无兼容路径);codec 注册制编解码器(json 内置 + form 表单,DoCodec 请求按 codec 序列化、响应按 Content-Type 反查回退请求 codec);v0.6.0 起 net/http 桥接层 NewRoundTripper/StdClient——存量标准库外呼一行 Transport 接入熔断/重试/指标/链路(重试缺省关显式开、非 2xx 非错误、hop-by-hop 隔离,#8);v0.8.0 起 SafeDialFunc/WithSafeDial 出站 SSRF 防护胶水(判定单源 pkg/urlguard v0.15.0:解析 IP 全量预检 + 实际 RemoteAddr 端点核对闭合重解析 TOCTOU、Happy-Eyeballs 覆盖,构造态豁免;v0.7.0 内置的独立 httpx/urlguard 实现收敛删除,#11) concurrency、logx、metrics、pkg、registry、tracing
network 轻量网络客户端——WebSocket/NATS/MQ 抽象、连接与流工具 concurrency、logx、tracing
timertask 分片定时任务(FNV-1a、心跳续约);v0.1.5 起可选可靠投递 DeliveryAtLeastOnce(租约 + Ack + 可见性超时;过期租约回扫重投/死信、停机 best-effort 收租,v0.1.7 接入生命周期真正生效);v0.2.0 起 Redis 后端迁至嵌套模块 timertask/redis(根包不再引 go-redis/logx/concurrency,require 仅 pkg——import timertask 与 import timertask/local 的模块依赖面均无 redis,纯 stdlib 嵌入式场景可用)+ 新增 local 子包 cron 调度器 Scheduler(表达式复用 pkg/timex/cron 不另写一份;并发度/CatchUp 补跑/退出语义明确,FakeClock 假时钟驱动、触发点序列可确定性断言) pkg
middleware Hertz 标准中间件栈(模块根 mw,双层目录已收平)+ contrib 桥接 + hmw + SheddingMiddleware 自适应降载 + chainmw 子包(transport 感知框架无关中间件,基于 core/chain+transport:Recovery 带 panic 隔离上报钩子 WithRecoveryReporter、AccessLog skip+redact+自定义字段、MetadataPropagate——kitex 经 kitex/chainmw.ToKitex 复用同一实现,根包 Recovery/AccessLog 已薄壳化复用 chainmw 内核、下游 handler 可经 transport.FromServerContext 读协议无关传输信息);v0.6.0 起 accesslog 凭证路径脱敏(WithAccessLogRedactPathPrefixes)、ratelimit 默认 KeyFunc 去端口(连接桶→地址桶);v0.11.0 起 WithAccessLogSink 落库 sink(AccessRecord{Method,Path,Status,Latency,ClientIP,UserAgent,TraceID} 与日志同源、c 同传可读业务键、error/panic 只记 Warn 不影响主链路、SkipPaths 命中不触发)+ chainmw.WithSink 内核同构(kitex 随 ToKitex 获得)+ StackConfig.DisableResponseWrap 响应自管一键形态(TraceID→Recovery→AccessLog)与 AccessLogOptions 透传 + doc/README「适用边界」段(响应契约统一 vs handler 自管的推荐组合,#18);v0.12.0 起 AccessLog panic 恢复点产出记录(AccessRecord.Panic 标记 + Err 摘要,Hertz status 按 Recovery 缺省 500)后按原值重抛——panic 请求不再零访问记录,sink/kitex 桥接同步留痕,#20;v0.13.0 起 accesslog 内建日志可关闭/改道(WithAccessLogSilent 只采不写、WithAccessLogLogger 注入宿主 logger 避免双写;静默下 skip/redact/sink/panic 记录照常),内核 chainmw.WithSilent 同构(kitex 随 ToKitex 获得),#38 concurrency、core、logx、metrics、pkg、registry、tracing
k8s Kubernetes 客户端封装——RESTConfig/Clientset/DynamicClient 构建(显式 kubeconfig → in-cluster 自动 → KUBECONFIG/~/.kube/config 逐级回退,QPS/Burst 限流默认 50/100)+ leaderelection 子包 Lease 选主简化封装(OnStartedLeading/OnStoppedLeading/OnNewLeader 回调,主备单活,仅要求 Lease CRUD 权限) logx
nns NNS 服务治理客户端——provider 无关发现/配置/实例缓存 concurrency、core、logx、registry
nns-endpoint NNS 端点工厂——per-service 熔断、换实例重试、gRPC 池 httpx、logx、metrics、nns、registry
bootstrap 应用生命周期引导——New(WithServiceName/WithShutdownTimeout/WithBeforeServe) + Run(fn) + healthz;App.Ready/ReadyChan/IsReady 就绪门(幂等并发安全)——嵌套 bootstrap/nacos 自 v0.3.0 起 NNS 注册延迟到 Ready 之后(server 就绪 → userFn 触发 Ready → 才进注册中心)、关停序 nns 反注册先于 server 关闭;就绪门漏接时可观测——nacos 看门狗超时打 ERROR 点明「服务不会注册到注册中心」(WithReadyTimeout 可调)+ NNSComponent.RegistrationState() 暴露注册阶段(pending/registered/failed/skipped/shutdown);Metrics/Tracing 在嵌套模块 bootstrap/obs concurrency、core、logx
hertz Hertz 运行时集成(模块根扁平化)——日志桥接、生命周期、SSE/health/metrics、适配器、OTel、crudh/validatorh(基于 kitx/validator)/versionh(原 handler 微库收编);RunServer 变参就绪门 WithOnReady(全部 HostPort TCP 探活可建连后回调,探活超时按启动失败优雅关机;缺省超时 5s 经 WithReadyProbeTimeout 调整——探活目标是 server 自动取到的全部 HostPort,无探活地址覆盖选项(kitex 侧才有 WithReadyAddr)),配合 bootstrap Ready 实现 NNS 延迟注册;transportx(hertz→core/transport 同构适配器)+ chainmw 桥接 bootstrap、concurrency、core、gormx、kitx、logx、metrics、middleware、nns、pkg、registry、tracing
kitex Kitex 生态——日志桥接、治理热更、生命周期、BizError 桥接、kitexotel、istio(xDS 动态熔断,原 istio 微库收编);RunServer 变参就绪门(WithOnReady/WithReadyAddr/WithReadyProbeTimeout)+ chainmw.ToKitex(core/chain.Middleware → kitex 中间件,与 hertz 共享 middleware/chainmw 实现)+ transportx bootstrap、concurrency、core、logx、metrics、nns、pkg、registry
workflow 组合式流程编排——串行/并行/异步/条件分支 + Hook AOP concurrency、logx
kitex-examples Kitex echo 示例——client/server + IDL + 治理配置 bootstrap、core、kitex、logx、tracing

依赖严格自底向上;微库已合并为 pkg,旧微库仓已删除。

本仓在矩阵中的位置与依赖见上表「矩阵内依赖」列。历史沿革:源自 ekit 单体库 (git.enjoye.top/enjoydream/ekit v0.34.0,冻结维护),2026-10 拆分为独立 module。

License

MIT — 见 LICENSE

Documentation

Overview

Package bootstrap 定义组件化启动编排。

本文件保留组件接口与轻量组件;重量级组件(metrics/tracing/NNS) 位于嵌套 module:github.com/oekit/bootstrap/obs 与 github.com/oekit/bootstrap/nacos,以 BootstrapperOption 注入。

Package bootstrap 提供 oekit 微服务的启动编排——纯编排壳,零重依赖。

职责:App 生命周期(ctx/资源注册/后台任务)、组件化 Bootstrapper、 信号监听与优雅关闭、WorkerManager。

组件化接入

重量级能力以嵌套 module 形式提供,经 BootstrapperOption 函数注入:

import (
    "github.com/oekit/bootstrap"
    "github.com/oekit/bootstrap/nacos" // Nacos/NNS 注册(按需)
    "github.com/oekit/bootstrap/obs"   // Prometheus/OTel(按需)
)

b, _ := bootstrap.New(
    bootstrap.WithServiceName("my-service"),
    nacos.WithNNS(cfg.Nacos, cfg.Port, cfg.DepServices),
    obs.WithMetrics(),
    obs.WithTracing(tracing.Config{Endpoint: "otel:4317"}),
)
_ = b.Run(userFn)

第三方集成实现 Component 接口后经 WithComponent 注入即可。

多 Server 场景

启动多个 server,通过 AddResource 注册关闭函数(逆序执行):

app.AddResource("hertz", func(ctx context.Context) error { return h.Shutdown(ctx) })

BeforeServe(引擎启动前)

WithBeforeServe(fn) 在组件初始化后、userFn 之前执行;失败中止启动。 用于 listener fd 收养、pid 交接等「必须在 serve 前」的动作。bootstrap 不接管 listener——自备时用 server.WithListener(ln) 即可。

attach 模式(NewApp)

bootstrap.NewApp(mgr *shutdown.Manager) 是 **attach 模式**入口:只把 app-lifecycle 四阶段挂到调用方的 shutdown.Manager 上,**不接管信号与 server**——不调用 signal.Notify、不调用 h.Spin,SIGINT/SIGTERM 与服务 启停由宿主负责。适合宿主已有 Spin/OnShutdown 生命周期(如 hertz), 只想补上「ctx 取消 + 资源关闭 + 后台任务 drain」的场景:

teardown := shutdown.New(30 * time.Second)
app := bootstrap.NewApp(teardown)
app.GoBackground("outbox", outbox.Run)
app.AddResource("redis", func(ctx context.Context) error { return rdb.Close() })
h.OnShutdown = append(h.OnShutdown, func(ctx context.Context) {
    teardown.Shutdown(ctx)
})
h.Spin()

四阶段 job 名为 app-lifecycle:{cancel,drain,close,wait},按注册顺序在 mgr.Shutdown 时执行;caller 自己 mgr.Add 的任务同样按注册顺序插入: NewApp 之前 Add 的排在四阶段前,之后 Add 的排在四阶段后。AddResource 的 closer 在 close 阶段逆序释放;GoBackground 的 goroutine 在 wait 阶段 被等待退出。

Index

Constants

This section is empty.

Variables

View Source
var ErrInvalidInterval = errors.New("worker: ticker interval must be > 0")

ErrInvalidInterval 在 AddTicker 的 interval <= 0 时返回(#101): 注册被拒绝(fail-fast)。time.NewTimer/NewTicker 对非正 interval panic, 且 panic 发生在内层 recover(只包住 fn)之外,被 GoSafe 出口 recover 兜住后表现为:wg 计数平衡、调用方无报错、任务从未运行且无日志。

View Source
var ErrManagerClosed = errors.New("worker: manager closed")

ErrManagerClosed 在 WorkerManager 已 Close 后注册任务(AddTicker/AddOnce) 时返回(#103):任务不会执行,注册被显式拒绝并打 Warn 日志留痕—— 此前是无返回值的静默丢弃,组件 Init 撞上关停竞态窗口时任务永不执行 且排障无任何线索。

Functions

This section is empty.

Types

type App

type App struct {
	// contains filtered or unexported fields
}

App 是应用生命周期管理器,统一管理资源注册、后台任务和优雅关闭。

用法:

app := bootstrap.NewApp(shutdownMgr)
app.AddResource("db", db.Close)
app.GoBackground("poller", func(ctx context.Context) { ... })
// shutdown 时自动按注册逆序关闭所有资源和等待后台任务退出

func NewApp

func NewApp(mgr *lifecycle.Manager) *App

NewApp 创建应用生命周期管理器,挂到调用方传入的 lifecycle.Manager 上(attach 模式)。

契约:NewApp 只做生命周期编排,**不接管信号与 server**——不调用 signal.Notify、不调用 h.Spin/RunServer;SIGINT/SIGTERM 与服务启停 一律由宿主(如 hertz.Server + OnShutdown)负责,宿主停机回调里再调 mgr.Shutdown 即可。

四阶段顺序语义:NewApp 立即向 mgr 注册 app-lifecycle:{cancel,drain,close,wait} 四个 job;lifecycle.Manager 按 **注册顺序**执行,因此调用方在 NewApp **之前** mgr.Add 的任务先于四阶段执行, 在 NewApp **之后** mgr.Add 的任务排在四阶段之后。应用自身资源请用 AddResource/GoBackground(分别落在 close / wait 阶段),不要在 NewApp 之后 直接 mgr.Add(会排到 wait 之后,错过 close 窗口)。

func (*App) AddPreCloseHook added in v0.7.6

func (a *App) AddPreCloseHook(name string, hook func(ctx context.Context) error)

AddPreCloseHook 注册「资源关闭之前」执行的关闭钩子(#168)。

执行窗口:app-lifecycle:close 阶段开始处——drain(进行中请求排空)之后、 closeAll(AddResource 资源逆序关闭)之前。面向 flush 指标/写 WAL 摘要/ 释放许可证这类「依赖资源必须仍在位」的收尾逻辑:它晚于普通 mgr.Add 注册的 job(那会排到 app-lifecycle:wait 之后、资源已关),早于资源关闭。

语义:按注册顺序执行;每个钩子 panic 隔离(转错误后继续后续钩子); 钩子错误与资源关闭错误同口径聚合进关停错误(#80 的上抛链路)。无独立 超时预算,受 close 阶段预算(WithShutdownTimeout)约束——需要逐钩子 预算时用 AddResourceTimeout 并自己编排。

GoBackground 后台任务的排空在 wait 阶段(其后)——钩子执行时后台任务 可能仍在收尾,钩子里访问「后台任务共写的共享状态」需自行同步。

func (*App) AddResource

func (a *App) AddResource(name string, closer func(context.Context) error)

AddResource 注册需要在关闭时释放的资源。 name 用于日志标识,closer 是关闭函数。

closer 在 close 阶段按注册逆序执行,无独立超时预算(受 Manager 的 close job 超时约束)。需要逐资源预算隔离(防止单个资源卡死吃光后续 清理预算)时用 AddResourceTimeout(#75)。

关停边界(#99):关闭序列启动后(close 阶段收敛循环结束前)注册的 closer 仍会被执行;close 阶段已结束后注册的 closer 永远不会执行 (打 ERROR 留痕)。

func (*App) AddResourceTimeout added in v0.6.15

func (a *App) AddResourceTimeout(name string, timeout time.Duration, closer func(context.Context) error)

AddResourceTimeout 注册需要在关闭时释放的资源,并为其设置独立的关闭 超时预算 timeout(#75)——App 用户直接获得 core/lifecycle.Manager 的 per-job 预算隔离语义,不必再手写 Manager↔App 适配层。

预算语义(对齐 Manager 的 per-job 超时):

  • 预算完全独立(#135):从 Background 派生,不与 close 阶段共享预算, 也不被 close job 的预算(WithShutdownTimeout,默认 10s)封顶—— timeout 大于 WithShutdownTimeout 时完整生效(文档示例「outbox 排空 20s」在默认配置下成立)。代价是 close 阶段总时长可能超过 WithShutdownTimeout(逐资源预算隔离 #75 的既定语义:总时长上界 = 各资源预算之和)。调用方 Shutdown ctx 的取消同样不截断预算化 closer;无独立预算的 closer(AddResource)仍受 close job 预算约束。
  • 前序资源的耗时不会挤占本资源的预算。典型场景(octopus 停机编排): outbox 排空 20s / otel 摘除 10s / nns 注销 10s 三阶段独立预算—— 共享预算时 outbox 卡住会吃光 nns 注销的预算,产生「已注册但永不 反注册」的幽灵实例。
  • 超时是协作式的:到点时 closer 收到已取消的 ctx,错误经 close 阶段 聚合上抛(#80);不感知 ctx 的 closer 无法被强杀,仍会阻塞 close 阶段。
  • timeout <= 0 表示不单独限时(等同 AddResource)。

关停边界(#99):关闭序列启动后(close 阶段收敛循环结束前)注册的 closer 仍会被执行;close 阶段已结束后注册的 closer 永远不会执行 (打 ERROR 留痕)。

func (*App) Context

func (a *App) Context() context.Context

Context 返回应用的根 context,可传递给后台任务。

func (*App) Fail added in v0.7.0

func (a *App) Fail(err error)

Fail 报告致命错误并触发关停(#133):fail-fast 原语——等价 RequestShutdown 的关停触发,但错误经 Bootstrapper.Run 的返回值上抛 (进程退出码非 0),且 Run 阻塞在等待信号/userFn 返回时被立即解除, userFn 无需监听 ctx(裸 h.Spin() 形态)即可被中止。

适用:启动期发现「服务不可用」必须早死(如延迟注册失败且 Required、 就绪门超时且 ReadyFatal)——仅 RequestShutdown 的取消语义表达不了失败, 部署门禁 / systemd Restart=on-failure / 对账链路会把启动失败当「成功」。

语义:

  • err == nil:仅触发关停(等同 RequestShutdown),不上抛错误;
  • 首个非 nil 错误生效(幂等,后续调用不覆盖;并发安全);
  • userFn 已返回后调用同样生效:错误并入 Run 的返回值(runErr 优先, fatal 以「; fatal: 」附加,与 #80 的关停错误同级)。

Run 返回后 Fail 不再有任何效果(进程退出在即,错误已并入返回值)。

func (*App) GoBackground

func (a *App) GoBackground(name string, fn func(ctx context.Context))

GoBackground 启动一个带 panic recovery 的后台 goroutine。 关闭时通过 ctx 取消信号通知退出,并等待所有后台任务完成。

关停边界(#104):shutdown 序列启动(app-lifecycle:cancel 阶段)之后再 调用会被拒绝并打 Warn 日志——fn 不执行。此前该窗口内的 wg.Add(正 delta)与 wait 阶段 timedWait 的 wg.Wait 并发,属于 sync.WaitGroup 规则 的未定义行为(counter 为零时 Wait 运行中发起 Add,可能 panic 或漏等)。

func (*App) IsReady added in v0.6.0

func (a *App) IsReady() bool

IsReady 报告当前就绪状态。

func (*App) Ready added in v0.6.0

func (a *App) Ready()

Ready 标记应用就绪(server 已监听、健康检查通过)。幂等,并发安全。 延迟注册型组件(如 bootstrap/nacos 的 NNS 组件)在收到就绪信号后 才执行注册——服务须在 server 就绪后调用本方法,否则不会进入注册中心。

func (*App) ReadyChan added in v0.6.0

func (a *App) ReadyChan() <-chan struct{}

ReadyChan 返回就绪信号;未 Ready 时阻塞,已 Ready 立即返回。

func (*App) RequestShutdown

func (a *App) RequestShutdown()

RequestShutdown 请求应用进入关闭流程:取消根 context,解除 userFn 中 `<-app.Context().Done()` 的阻塞,使其返回并触发 lifecycle.Manager 的优雅 关闭序列。幂等;在关闭序列中再次调用是安全的(cancel 本身幂等)。 典型用途见 lifecycle.App.RequestShutdown。

注意:纯取消语义不携带失败原因——userFn 不监听 ctx(裸 h.Spin())时 Run 永不返回,userFn 监听 ctx 且返回 nil 时 Run 返回 nil(退出码 0)。需要 「中止且退出码非 0」的 fail-fast 语义用 Fail(#133)。

func (*App) SetWaitTimeout

func (a *App) SetWaitTimeout(d time.Duration)

SetWaitTimeout 设置 GoBackground goroutine 的 drain 超时。 超时后 wg.Wait 被跳过,日志警告仍在运行的 goroutine 数量。 默认 30s;传 0 禁用超时(无限等待,仅在确信所有 goroutine 会自行退出时使用)。

drain 时长独立于 WithShutdownTimeout(#134):wait job 在 lifecycle.Manager 上不设预算,「0 = 无限等待」与默认 30s 都不会被 Manager 的默认 job 预算 (Bootstrapper 下即 WithShutdownTimeout)静默截断。中止源只有两个:本 超时到点,与调用方显式取消 Shutdown 的 ctx。

type Bootstrapper

type Bootstrapper struct {
	// contains filtered or unexported fields
}

Bootstrapper 是组件化应用编排器。

通过函数选项配置启动组件;重量级集成(Nacos/NNS、Prometheus/OTel) 位于嵌套 module,以 Option 函数形式注入,根包保持零重依赖:

b, err := bootstrap.New(
    bootstrap.WithServiceName("my-service"),
    nacos.WithNNS(nacosCfg, 8080, deps),   // github.com/oekit/bootstrap/nacos
    obs.WithMetrics(),                     // github.com/oekit/bootstrap/obs
    obs.WithTracing(tracing.Config{...}),
)
if err != nil { log.Fatal(err) }
b.Run(func(app *bootstrap.App) error {
    h := server.New(server.WithHostPorts(":8080"))
    app.AddResource("hertz", h.Shutdown)
    return h.Run()
})

本地开发和测试场景可不添加任何外部依赖组件。

func New

func New(opts ...BootstrapperOption) (*Bootstrapper, error)

New 创建 Bootstrapper 实例。

func (*Bootstrapper) AddComponent

func (b *Bootstrapper) AddComponent(c Component)

AddComponent 追加启动组件(动态注入)。 导出以支持嵌套 module 与第三方实现注入,与 WithComponent 等价。

ServiceNameAware 回填(#131):New 之后调用时就地补做——New 强制 serviceName 非空,此后挂载的组件(嵌套 module 动态注入、第三方实现) 与经 WithComponent 挂载的组件拿到服务名的时机一致,不再静默拿到空 服务名。New 之前(选项应用阶段)调用时 serviceName 尚未定,回填留给 New 的统一回填;两条路径可能对同一组件各执行一次回填(同值重复), ServiceNameAware 实现须幂等。

func (*Bootstrapper) Run

func (b *Bootstrapper) Run(userFn func(*App) error) error

Run 启动服务,阻塞直到收到关闭信号或 userFn 返回。

执行顺序:

  1. 创建 lifecycle.Manager 和 App

  2. 按注册顺序初始化所有 Component 2.5 BeforeServe 钩子(引擎启动前的定制准备)

  3. 调用 userFn(app) 启动用户服务——server 就绪后应调用 app.Ready() 触发就绪门:延迟注册型组件(如 bootstrap/nacos 的 NNS 组件, nacos/v0.3.0 起)在收到就绪信号后才注册,且关停时反注册先于 server 关闭执行(对齐 go-kratos Stop 语义)

    **就绪门漏接的可观测性**(bootstrap#33):漏调 app.Ready() 时服务 不会注册到注册中心——推荐经 hertz/kitex RunServer 的 WithOnReady 胶水触发;自建启动骨架(直接 h.Spin())必须自己调。漏接时 bootstrap/nacos 的就绪门看门狗会在超时后打 ERROR 日志点明此事, 并可用 nacos 组件的 RegistrationState() 查询注册状态。

  4. 监听 SIGINT/SIGTERM

  5. 等待:userFn 返回、收到信号、或组件经 App.Fail 触发 fail-fast (#133:致命错误经返回值上抛,退出码非 0;userFn 不监听 ctx 时 Run 也能被解除阻塞)

  6. 优雅关闭;关停完成后再等一个固定小窗口(lateUserFnErrGrace)补收 晚于关停完成才返回的 userFn 错误——界内不丢、界外如实声明丢弃 (#141,此前非阻塞读连关停期间返回的错误都接不住)

type BootstrapperOption

type BootstrapperOption func(*Bootstrapper)

BootstrapperOption 配置 Bootstrapper。

func WithBeforeServe added in v0.5.4

func WithBeforeServe(fn func() error) BootstrapperOption

WithBeforeServe 注册「引擎启动前」钩子:在组件初始化之后、userFn 之前 按注册顺序执行;任一返回 error 则中止启动(已初始化组件走优雅回滚)。

典型用途:零断点重启的 listener fd 收养(SCM_RIGHTS → restart.Adopt)、 pid 文件交接、启动前配置校验。这些动作必须发生在「进程已知自己角色」 之后、「HTTP 引擎开始 serve」之前——userFn 是引擎跑起来之后的逻辑, 装不下这段。

调用方自备 listener 时也可不经过本钩子,直接:

h := server.New(server.WithListener(ln), ...)

bootstrap 不接管 listener 的创建/收养,只提供时机保证。

func WithComponent

func WithComponent(c Component) BootstrapperOption

WithComponent 添加自定义组件——第三方集成注入的标准入口。

实现 Component 接口的任意类型都可通过本方法接入启动编排; 嵌套 module(bootstrap/nacos、bootstrap/obs)的 WithXxx 选项 内部即委托此方法。

func WithServiceName

func WithServiceName(name string) BootstrapperOption

WithServiceName 设置服务名称(必填)。

func WithShutdownHook

func WithShutdownHook(name string, hook func(ctx context.Context) error) BootstrapperOption

WithShutdownHook 添加自定义关闭钩子。

执行窗口(#168):drain 之后、AddResource 资源关闭**之前**——flush 指标/ 写 WAL 摘要/释放许可证类钩子的依赖资源(DB/连接)此刻仍在位。需要 「全部资源关闭与后台排空之后」的晚窗时用 WithComponent(&ShutdownHookComponent{..., Late: true})。

func WithShutdownTimeout

func WithShutdownTimeout(d time.Duration) BootstrapperOption

WithShutdownTimeout 设置优雅关闭超时,默认 10 秒。

type Component

type Component interface {
	// Name 返回组件标识,用于日志。
	Name() string
	// Init 执行组件初始化,可通过 app 和 mgr 注册资源和关闭钩子。
	Init(ctx context.Context, app *App, mgr *lifecycle.Manager) error
}

Component 是可插拔的启动组件接口。 每个组件负责一项启动任务(如指标注册、追踪初始化、服务注册等), 并可通过 mgr 注册关闭钩子。

type ServiceNameAware

type ServiceNameAware interface {
	SetServiceName(name string)
}

ServiceNameAware 需要感知服务名的组件实现此接口; New() 在应用完全部选项后统一回填(与 WithServiceName 的调用顺序无关), New 之后经 AddComponent 挂载的组件就地补做(#131)。实现须幂等—— 两条路径可能对同一组件各回填一次(同值)。 嵌套 module 中的组件实现它即可参与回填,根包无需感知具体组件类型。

type ShutdownHookComponent

type ShutdownHookComponent struct {
	ComponentName string
	Hook          func(ctx context.Context) error
	// Late 置 true 时钩子经 mgr.Add 注册,排到 app-lifecycle:wait 之后
	// (全部资源关闭与后台排空之后)执行;缺省 false = 前置关闭钩子。
	Late bool
}

ShutdownHookComponent 通用关闭钩子组件,用于注册自定义资源释放逻辑。

阶段位(#168):默认注册为**前置关闭钩子**(App.AddPreCloseHook)——在 drain 之后、AddResource 资源关闭之前执行,flush/WAL 摘要/许可证释放类 钩子的依赖资源(DB/连接)此刻仍在位。此前钩子经 mgr.Add 落到 app-lifecycle:wait 之后(资源已全部关闭),flush 在依赖已关后才跑, 是 App 文档自己点名的反模式;需要旧的「全部关闭之后」窗口时置 Late=true(钩子排在 app-lifecycle 四阶段之后执行)。

func (*ShutdownHookComponent) Init

Init 注册关闭钩子:缺省挂前置钩子窗(资源关闭之前,#168); Late=true 时保持旧位置(mgr.Add,四阶段之后)。

func (*ShutdownHookComponent) Name

func (c *ShutdownHookComponent) Name() string

Name 返回组件名称。

type WorkerManager

type WorkerManager struct {
	// contains filtered or unexported fields
}

WorkerManager 管理一组周期性后台任务。

用法:

wm := bootstrap.NewWorkerManager(ctx)
wm.AddTicker("timeout-checker", 60*time.Second, func(ctx context.Context) {
    buildSvc.CheckBuildTimeout(ctx)
    deploySvc.CheckDeployTimeout(ctx)
})
wm.AddTicker("metrics-reporter", 30*time.Second, func(ctx context.Context) {
    common.ReportQueueMetrics(ctx)
})
// ctx 取消时所有 worker 自动退出

func NewWorkerManager

func NewWorkerManager(ctx context.Context) *WorkerManager

NewWorkerManager 创建后台任务管理器。 ctx 取消或 Close() 调用时所有任务自动退出。

func (*WorkerManager) AddOnce

func (w *WorkerManager) AddOnce(name string, fn func(ctx context.Context)) error

AddOnce 注册一个一次性任务,在后台执行。 已 Close 后注册被拒绝并返回 ErrManagerClosed、打 Warn 日志(#103)—— 不再静默吞掉。

func (*WorkerManager) AddTicker

func (w *WorkerManager) AddTicker(name string, interval time.Duration, fn func(ctx context.Context)) error

AddTicker 注册一个周期性任务,按 interval 间隔执行 fn。 ctx 取消时自动退出。返回 error(#101/#103):注册被拒时可感知,库侧同步打日志留痕。

语义:

  • interval 必须为正:非正值直接拒绝注册并返回 ErrInvalidInterval(#101)。
  • 间隔是**完成驱动**的(#98):下一轮在上一轮 fn 返回后再等 interval。 fn 耗时超过 interval 时堆积的滴答被丢弃,不会背靠背补跑(旧实现的 ticker 通道缓冲 1,fn 返回时压着一枚堆积滴答,会立即再执行一轮—— 24h 间隔的 gc 类慢任务撞上一次超时运行,结束后立刻又跑一轮)。
  • 已 Close 后注册被拒绝并返回 ErrManagerClosed(#103)。
  • fn panic 被就地收编为 ERROR 日志,后续 tick 继续执行。

func (*WorkerManager) Close

func (w *WorkerManager) Close()

Close 取消 context、阻止新任务注册并**无限期**等待所有已启动的任务退出。 多次调用是安全的。fn 不感知 ctx(内嵌网络 IO/LLM 调用未传 ctx)时本方法 会永久阻塞——需要停机兜底请改用 CloseTimeout(#98)。

func (*WorkerManager) CloseTimeout added in v0.6.15

func (w *WorkerManager) CloseTimeout(d time.Duration)

CloseTimeout 是带超时的 Close(#98):最多等待 d,超时后放弃等待仍在 运行的任务、打 ERROR 日志留痕并返回——停机序列不再被不感知 ctx 的 fn 无限期挂死(调用方此前没有任何超时兜底可加,wg.Wait 裸暴露)。 被放弃的任务随进程退出回收;内部 wg.Wait 的 goroutine 有意泄漏 (sync.WaitGroup 的等待无法取消),与 App.timedWait 同一权衡。 d <= 0 等同 Close(无限等待)。多次调用是安全的(与 Close 混用亦然)。

func (*WorkerManager) Wait

func (w *WorkerManager) Wait()

Wait 等待所有任务退出。

Directories

Path Synopsis
Package healthz 提供 ekit 应用健康检查 helper,让业务注册多个 check 项(DB / Redis / 上游依赖等),/healthz endpoint 一次调起,全部通过返 200, 任一硬失败返 503;软依赖降级(Degraded)返 200 + status "degraded"。
Package healthz 提供 ekit 应用健康检查 helper,让业务注册多个 check 项(DB / Redis / 上游依赖等),/healthz endpoint 一次调起,全部通过返 200, 任一硬失败返 503;软依赖降级(Degraded)返 200 + status "degraded"。
nacos module
obs module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL