第一章:Go后端基础架构设计全景概览
现代Go后端系统并非单体进程的简单堆砌,而是由分层职责明确、松耦合且可观测的组件协同构成的有机整体。其核心目标是在高并发、低延迟与工程可维护性之间取得平衡,同时天然适配云原生环境。
核心分层结构
- 接入层:处理TLS终止、路由分发与限流熔断,常用Nginx或Envoy,亦可基于Go标准库
net/http或gin/echo构建轻量网关 - 服务层:业务逻辑主干,采用清晰的Clean Architecture或Hexagonal模式,分离领域模型、用例(Use Case)与传输适配器
- 数据访问层:抽象数据库、缓存与消息队列交互,推荐使用
sqlc生成类型安全SQL查询,搭配redis-go与goclient统一客户端接口 - 基础设施层:日志(
zerolog)、指标(prometheus/client_golang)、链路追踪(go.opentelemetry.io/otel)三者集成,形成可观测性基座
关键设计原则
- 无状态优先:所有服务实例应可随时伸缩或替换,会话状态交由Redis或外部存储托管
- 显式错误处理:拒绝
panic跨goroutine传播,统一使用errors.Join组合错误上下文,HTTP handler中通过中间件标准化响应格式 - 配置驱动:使用
spf13/viper加载YAML/TOML配置,支持环境变量覆盖,避免硬编码
快速启动示例
以下代码片段展示一个具备健康检查、结构化日志与Prometheus指标的基础服务骨架:
package main
import (
"log"
"net/http"
"os"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
// 初始化结构化日志(输出到stdout)
zerolog.SetGlobalLevel(zerolog.InfoLevel)
log.Logger = log.With().Caller().Logger()
// 注册默认指标收集器
prometheus.MustRegister(collectors.NewBuildInfoCollector())
prometheus.MustRegister(collectors.NewGoCollector())
// 健康检查端点
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
// 指标端点
http.Handle("/metrics", promhttp.Handler())
addr := os.Getenv("ADDR")
if addr == "" {
addr = ":8080"
}
log.Info().Str("addr", addr).Msg("Starting HTTP server")
log.Fatal().Err(http.ListenAndServe(addr, nil)).Send()
}
该骨架可直接运行,暴露/health与/metrics端点,为后续业务模块注入提供坚实起点。
第二章:HTTP Server 构建与高可用实践
2.1 标准 net/http 与 fasthttp 的选型对比与性能压测
Go 生态中,net/http 是官方标准库,而 fasthttp 是高性能替代方案,二者在内存模型与请求生命周期上存在根本差异。
设计哲学差异
net/http:面向对象,每请求分配*http.Request和*http.Response,堆分配频繁fasthttp:基于字节缓冲复用(*fasthttp.RequestCtx),零堆分配关键路径
基准压测结果(16核/32GB,wrk -t4 -c100 -d30s)
| 框架 | RPS | 平均延迟 | 内存分配/req |
|---|---|---|---|
| net/http | 28,400 | 3.2 ms | 12.4 KB |
| fasthttp | 89,700 | 1.1 ms | 184 B |
// fasthttp 简洁 Handler 示例
func handler(ctx *fasthttp.RequestCtx) {
ctx.SetStatusCode(fasthttp.StatusOK)
ctx.SetContentType("application/json")
ctx.WriteString(`{"msg":"hello"}`)
}
该写法避免字符串转 []byte 临时分配;ctx 复用底层 bufio.Reader/Writer,无 GC 压力。而等效 net/http 实现需构造 ResponseWriter 接口并触发多次堆分配。
性能归因流程
graph TD
A[HTTP 请求到达] --> B{协议解析}
B --> C[net/http:新建 Request/Response 对象]
B --> D[fasthttp:复用 RequestCtx 缓冲区]
C --> E[GC 频繁触发]
D --> F[零分配核心路径]
2.2 路由设计:Gin/Echo/Chi 的中间件链与上下文传递机制
中间件执行顺序决定上下文生命周期
Gin、Echo、Chi 均采用洋葱模型,但上下文(*gin.Context / echo.Context / chi.Context)的封装方式差异显著:
- Gin:
Context是结构体指针,中间件通过c.Next()显式控制流程,c.Set("key", val)存入 map; - Echo:
Context是接口,依赖c.Set()+c.Get(),底层用sync.Map提升并发安全; - Chi:
Context是context.Context的扩展,通过chi.NewRouteContext()构建,ctx.Value()传递键值对。
上下文数据传递对比
| 框架 | 上下文类型 | 传值方法 | 并发安全 | 生命周期绑定 |
|---|---|---|---|---|
| Gin | *gin.Context |
c.Set()/c.MustGet() |
否(需手动保护) | 请求周期 |
| Echo | echo.Context |
c.Set()/c.Get() |
是 | 请求周期 |
| Chi | chi.Context |
ctx.Value()/WithValue() |
是(基于 context.Context) |
请求周期 |
// Gin 中间件链示例:日志 → 认证 → 路由处理
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if !isValidToken(token) {
c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
return
}
c.Set("userID", extractUserID(token)) // 注入到 Context map
c.Next() // 执行后续中间件或 handler
}
}
逻辑分析:
c.Set()将userID写入c.Keys(map[string]interface{}),后续 handler 通过c.MustGet("userID")安全读取。c.Next()是控制权移交点,若不调用则中断链式执行;c.Abort()阻止后续中间件运行,但不终止当前函数。
graph TD
A[Request] --> B[Logger MW]
B --> C[Auth MW]
C --> D[RateLimit MW]
D --> E[Handler]
E --> F[Response]
C -.->|c.AbortWithStatusJSON| G[Early Response]
2.3 请求生命周期管理:从连接复用、TLS 配置到请求限流与熔断
现代 HTTP 客户端需精细管控请求全链路——始于连接复用,成于安全握手,稳于流量治理。
连接复用与 TLS 优化
启用 keep-alive 与会话复用可显著降低 RTT 开销。以下为 Go http.Client 典型配置:
client := &http.Client{
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 30 * time.Second,
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.X25519, tls.CurvesSupported[0]},
SessionTicketsDisabled: true, // 避免会话票证泄露风险
},
},
}
MaxIdleConnsPerHost 控制单主机最大空闲连接数,防止连接池膨胀;TLSClientConfig 中禁用会话票证(SessionTicketsDisabled: true)提升前向安全性,配合 X25519 优先协商加速密钥交换。
流量控制双支柱
| 机制 | 触发条件 | 响应动作 |
|---|---|---|
| 请求限流 | QPS 超阈值 | 返回 429,拒绝新请求 |
| 熔断器 | 连续失败率 > 50% | 自动跳闸,降级 fallback |
graph TD
A[HTTP 请求] --> B{连接池获取}
B -->|复用空闲连接| C[TLS 会话复用]
B -->|新建连接| D[TLS 握手]
C & D --> E[限流检查]
E -->|通过| F[发送请求]
E -->|拒绝| G[返回 429]
F --> H{响应失败率统计}
H -->|熔断触发| I[开启熔断状态]
2.4 接口标准化:OpenAPI 3.0 自动生成与 Swagger UI 集成实战
现代微服务架构中,接口契约先行已成为协作基石。Springdoc OpenAPI 3.0 可基于注解自动推导 API 元数据,无需手动维护 YAML。
集成核心依赖
<!-- Maven -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
<version>2.3.0</version>
</dependency>
该依赖替代旧版 springfox,内建对 Spring Boot 3+ 和 Jakarta EE 9+ 的原生支持,自动注册 /v3/api-docs(JSON)与 /swagger-ui.html(交互式 UI)端点。
关键配置项
| 配置项 | 说明 |
|---|---|
springdoc.api-docs.path |
自定义 OpenAPI JSON 路径,默认 /v3/api-docs |
springdoc.swagger-ui.enabled |
启用 Swagger UI,默认 true |
springdoc.model-converters.enabled |
是否启用自定义模型转换器 |
文档增强示例
@Operation(summary = "创建用户", description = "返回新用户的完整信息")
@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody @Valid User user) {
return ResponseEntity.ok(userService.save(user));
}
@Operation 补充语义元数据;@Valid 触发自动参数 Schema 生成;ResponseEntity<T> 被准确映射为 200 OK 响应体 Schema。
graph TD A[Controller 方法] –> B[Springdoc 注解扫描] B –> C[构建 OpenAPI 3.0 Document 对象] C –> D[/v3/api-docs JSON 输出] C –> E[Swagger UI 渲染]
2.5 多协议支持扩展:gRPC-HTTP Gateway 与 REST/gRPC 双栈统一治理
现代微服务需同时响应移动端(REST/JSON)与内部服务间(gRPC/Protobuf)调用。gRPC-HTTP Gateway 作为反向代理层,将 HTTP/1.1 请求动态翻译为 gRPC 调用,实现双栈共存。
核心工作流
# grpc-gateway 生成配置示例(protoc-gen-openapiv2)
http:
rules:
- selector: example.v1.UserService.GetProfile
get: /v1/users/{id}
# 将 URL path 中的 {id} 自动映射到 proto message 字段
该配置驱动 protoc 插件在生成 Go 代码时注入 HTTP 路由绑定逻辑,id 被解析并注入 GetProfileRequest.Id 字段,避免手动参数提取。
协议治理能力对比
| 能力 | REST-only | gRPC-only | 双栈统一网关 |
|---|---|---|---|
| 请求验证 | ✅(OpenAPI) | ✅(proto validation) | ✅(双重校验) |
| OpenAPI 文档自动生成 | ✅ | ❌ | ✅(基于 proto) |
流量路由示意
graph TD
A[HTTP Client] -->|/v1/users/123| B(gRPC-HTTP Gateway)
B -->|Unary RPC| C[UserService gRPC Server]
C -->|Proto response| B
B -->|JSON marshaled| A
第三章:配置中心的动态化与安全治理
3.1 基于 Viper 的多源配置加载与热重载实现原理
Viper 支持从多种源头(文件、环境变量、远程 etcd、flag)统一抽象配置,核心在于 viper.AddConfigPath() 与 viper.SetConfigType() 的协同调度。
配置源优先级策略
- 环境变量 > 命令行参数 > 远程键值存储 > 配置文件
- 同名键以高优先级源为准,实现“覆盖式合并”
热重载触发机制
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Printf("Config updated: %s", e.Name)
})
WatchConfig()内部启动fsnotify.Watcher监听文件系统事件;OnConfigChange注册回调,仅在e.Op&fsnotify.Write != 0时触发重解析。需确保viper.ReadInConfig()已成功执行一次,否则Unmarshal()将 panic。
支持的配置格式对比
| 格式 | 加密支持 | 内嵌结构 | 环境变量展开 |
|---|---|---|---|
| YAML | ✅(需插件) | ✅ | ✅ |
| JSON | ❌ | ✅ | ❌ |
| TOML | ✅ | ✅ | ✅ |
graph TD
A[启动 WatchConfig] --> B{文件变更?}
B -->|是| C[触发 fsnotify.Event]
C --> D[调用 OnConfigChange 回调]
D --> E[自动 ReadInConfig + Unmarshal]
E --> F[更新内存中 viper.viper 结构体]
3.2 配置加密与敏感信息隔离:KMS 集成与环境变量安全注入
现代应用需避免硬编码密钥、数据库密码等敏感数据。KMS(如 AWS KMS 或 HashiCorp Vault)提供集中式密钥生命周期管理,配合运行时解密实现“静态加密、动态解密”。
安全注入模式对比
| 方式 | 静态风险 | 注入时机 | 是否支持轮转 |
|---|---|---|---|
| 环境变量明文 | 高 | 启动前 | 否 |
| KMS 加密后挂载 | 低 | 启动时解密 | 是 |
| Init Container 解密 | 最低 | 主容器前 | 是 |
KMS 解密注入示例(AWS)
# 使用 aws-cli 在容器启动时解密并注入环境变量
export DB_PASSWORD=$(aws kms decrypt \
--ciphertext-blob fileb://encrypted-db-pass.bin \
--query 'Plaintext' --output text | base64 -d)
逻辑说明:
--ciphertext-blob指向 Base64 编码的密文二进制文件;--query 'Plaintext'提取解密后的 Base64 字符串;base64 -d还原原始密码。该操作依赖 IAM 角色最小权限策略,确保仅限目标密钥解密。
graph TD
A[应用容器] -->|请求解密| B(KMS 服务)
B -->|返回 Plaintext| C[Init Container]
C -->|注入 env| D[主应用进程]
3.3 分布式配置同步:etcd/vault 实时监听与版本回滚机制
数据同步机制
etcd 使用 Watch API 实现毫秒级配置变更通知,Vault 则依赖 vault kv get -version=N 结合轮询或事件驱动 Webhook。
# 监听 etcd 中 /config/app/ 的所有变更(支持递归)
etcdctl watch --prefix "/config/app/" --rev=12345
该命令从指定 revision 开始持续监听,--prefix 启用路径前缀匹配,--rev 避免漏掉历史变更;客户端需自行解析 JSON 格式事件流并触发本地热重载。
版本回滚能力对比
| 系统 | 回滚粒度 | 原子性 | 自动审计日志 |
|---|---|---|---|
| etcd | key 级别 | ✅(事务支持) | ❌(需外部集成) |
| Vault | 路径级(kv-v2) | ✅(版本化写入) | ✅(内置 audit log) |
回滚执行流程
graph TD
A[检测配置异常] --> B{选择回滚源}
B -->|etcd| C[查询历史 revision]
B -->|Vault| D[调用 /v1/kv/metadata/<path> 获取版本列表]
C --> E[etcdctl put --prev-kv ...]
D --> F[vault kv get -version=3 ...]
回滚操作必须结合服务健康检查闭环验证,避免配置恢复但服务未就绪。
第四章:日志、链路与可观测性体系构建
4.1 结构化日志设计:Zap 日志分级、字段注入与采样策略
Zap 通过 zapcore.LevelEnablerFunc 实现细粒度日志分级控制,支持动态调整不同模块的输出级别。
字段注入:上下文增强
logger := zap.With(
zap.String("service", "auth"),
zap.Int("version", 2),
zap.String("trace_id", traceID), // 动态注入追踪标识
)
logger.Info("user login succeeded") // 自动携带全部字段
逻辑分析:zap.With() 返回新 logger,所有后续日志自动附加字段;字段在编码前序列化,零拷贝写入,避免运行时反射开销。
采样策略对比
| 策略 | 适用场景 | 采样率控制方式 |
|---|---|---|
NewSampler |
高频 INFO 日志 | 固定窗口计数(如 100:1) |
NewCountSampler |
调试级日志 | 按调用次数限流 |
日志分级流程
graph TD
A[Log Call] --> B{Level >= Enabled?}
B -->|Yes| C[Encode Fields]
B -->|No| D[Drop]
C --> E[Apply Sampler]
E --> F[Write to Sink]
4.2 全链路追踪:OpenTelemetry SDK 集成与 Jaeger/Tempo 后端对接
OpenTelemetry(OTel)作为云原生可观测性标准,其 SDK 提供语言无关的 API 与 SDK 分离设计,支持无缝对接多种后端。
SDK 初始化与导出器配置
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
provider = TracerProvider()
exporter = OTLPSpanExporter(
endpoint="http://jaeger:4318/v1/traces", # Jaeger HTTP 接收端(兼容 OTLP)
# 或 endpoint="http://tempo:4318/v1/traces"(Tempo 同样支持 OTLP/HTTP)
)
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)
该代码初始化 OTel SDK 并注册
BatchSpanProcessor,将采样后的 span 批量推送至 Jaeger/Tempo 的 OTLP HTTP 端点;endpoint路径需与后端部署方式对齐(Jaeger v1.52+、Tempo v2.0+ 均原生支持 OTLP/HTTP)。
后端兼容性对比
| 后端 | 协议支持 | 部署轻量性 | 查询能力 |
|---|---|---|---|
| Jaeger | OTLP/gRPC, HTTP | ⭐⭐⭐⭐ | 基于服务/操作过滤 |
| Tempo | OTLP/HTTP, gRPC | ⭐⭐⭐⭐⭐ | 支持 TraceQL 高级检索 |
数据同步机制
graph TD
A[Instrumented App] -->|OTLP/HTTP| B(Jaeger Collector)
A -->|OTLP/HTTP| C(Tempo Distributor)
B --> D[(Elasticsearch/Cassandra)]
C --> E[(Object Storage: S3/GCS)]
4.3 指标采集与监控:Prometheus Exporter 自定义指标埋点与 Grafana 看板搭建
自定义指标埋点(Go Exporter 示例)
// 定义一个带标签的直方图,用于统计 HTTP 请求延迟(单位:毫秒)
httpRequestDuration := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_ms",
Help: "HTTP request duration in milliseconds",
Buckets: []float64{10, 50, 100, 200, 500, 1000},
},
[]string{"method", "endpoint", "status_code"},
)
prometheus.MustRegister(httpRequestDuration)
// 在请求处理结束时记录
httpRequestDuration.WithLabelValues(r.Method, r.URL.Path, strconv.Itoa(w.Status())).Observe(latencyMs)
该代码注册了一个带 method/endpoint/status_code 三维度标签的直方图。Buckets 显式定义分位统计区间,便于后续计算 P90/P99;WithLabelValues 动态绑定运行时标签,避免指标爆炸。
Grafana 面板关键配置项
| 字段 | 值 | 说明 |
|---|---|---|
| Data source | Prometheus | 必须指向已配置的 Prometheus 实例 |
| Query | rate(http_request_duration_ms_sum[5m]) / rate(http_request_duration_ms_count[5m]) |
计算平均延迟(毫秒) |
| Legend | {{method}} {{endpoint}} |
自动展开 Prometheus 标签为图例 |
数据流拓扑
graph TD
A[应用代码埋点] --> B[Exporter HTTP 端点]
B --> C[Prometheus scrape]
C --> D[TSDB 存储]
D --> E[Grafana 查询渲染]
4.4 日志-链路-指标三元关联:TraceID 注入、Context 透传与异常根因定位实践
在微服务调用中,统一 TraceID 是实现日志、链路追踪与监控指标关联的基石。需在入口(如 HTTP 请求)生成并注入 X-B3-TraceId,并通过线程上下文(如 ThreadLocal 或 Scope)全程透传。
TraceID 自动注入示例(Spring Boot)
@Component
public class TraceIdFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
String traceId = request.getHeader("X-B3-TraceId");
if (traceId == null || traceId.isBlank()) {
traceId = UUID.randomUUID().toString().replace("-", "");
}
MDC.put("traceId", traceId); // 绑定至日志上下文
try {
chain.doFilter(req, res);
} finally {
MDC.remove("traceId"); // 防止线程复用污染
}
}
}
逻辑分析:该过滤器拦截所有 HTTP 请求,优先提取上游传递的 X-B3-TraceId;若缺失则生成新 ID,并通过 SLF4J 的 MDC 注入日志上下文,确保后续 log.info("order processed") 自动携带 traceId=xxx 字段。
上下文透传关键机制
- 跨线程:使用
TransmittableThreadLocal替代ThreadLocal - 跨 RPC:OpenFeign 拦截器自动复制
MDC.get("traceId")到请求头 - 跨异步:
CompletableFuture需显式copy上下文
根因定位典型流程
| 步骤 | 动作 | 工具/能力 |
|---|---|---|
| 1 | 从告警指标(如 P99 延迟突增)获取时间窗口与服务名 | Prometheus + Alertmanager |
| 2 | 关联该时段内含相同 TraceID 的全链路 Span 和业务日志 | Jaeger + ELK |
| 3 | 定位耗时最长 Span 及其下游异常日志(如 ERROR traceId=abc123 DB timeout) |
日志关键字 + TraceID 联查 |
graph TD
A[HTTP Gateway] -->|X-B3-TraceId| B[Order Service]
B -->|MDC + Feign Interceptor| C[Payment Service]
C -->|AsyncTask + TTL| D[Notification Service]
D --> E[(Log: traceId=abc123 ERROR)]
第五章:健康检查、优雅退出与生命周期终局保障
容器化服务的健康检查实战配置
在 Kubernetes 生产环境中,livenessProbe 与 readinessProbe 的配置差异直接影响系统稳定性。某电商订单服务曾因将数据库连接检查误配为 livenessProbe,导致 MySQL 短暂抖动时 Pod 被反复重启,订单积压激增 300%。正确做法是:readinessProbe 检查 /health/ready(验证 DB 连接 + Redis 可写),livenessProbe 仅检查 /health/live(轻量级进程存活心跳)。以下为 Helm values.yaml 片段:
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 3
优雅退出的信号处理链路
Java 应用需捕获 SIGTERM 并完成三阶段退出:① 关闭 HTTP 端口(拒绝新请求);② 等待正在处理的请求完成(设置 30s 超时);③ 释放数据库连接池与 Kafka 消费者。Spring Boot 2.3+ 提供 server.shutdown=graceful 配置,但必须配合 spring.lifecycle.timeout-per-shutdown-phase=30s 才能生效。未配置超时会导致 Pod 被强制 SIGKILL 终止。
生命周期终局保障的双保险机制
某金融对账服务在节点驱逐时出现数据丢失,根源在于未实现终局保障。解决方案采用双保险:
- 应用层:注册
Runtime.addShutdownHook(),执行flushPendingRecords()+awaitTermination(45, SECONDS); - 基础设施层:Kubernetes 设置
preStopHook 与terminationGracePeriodSeconds: 60,确保容器有足够时间完成钩子逻辑。
| 组件 | 超时设置 | 实际耗时(平均) | 风险点 |
|---|---|---|---|
| HTTP 请求等待 | 30s | 22s | 无超时则阻塞退出 |
| Kafka 消费者提交 | 15s | 8s | 提交失败导致重复消费 |
| 数据库事务回滚 | 10s | 4s | 长事务需提前预警 |
健康端点的可观测性增强
/health/ready 不应仅返回 HTTP 200,而需携带结构化诊断信息。某支付网关改造后返回 JSON:
{
"status": "UP",
"components": {
"db": {"status": "UP", "details": {"poolActive": 12, "maxWaitMs": 15}},
"redis": {"status": "UP", "details": {"latencyMs": 2.3}},
"kafka": {"status": "DOWN", "details": {"error": "timeout after 5000ms"}}
}
}
该格式被 Prometheus blackbox_exporter 解析后,自动生成 health_component_status{component="kafka"} 指标,触发告警时可直接定位故障组件。
终局保障的混沌工程验证
使用 Chaos Mesh 注入 PodChaos 故障,模拟节点突然不可用场景。通过日志分析发现:73% 的 Pod 在 preStop 执行期间仍接收新请求——根本原因是 Service 的 externalTrafficPolicy: Cluster 导致流量未及时切断。修复后将策略改为 Local,并增加 iptables -A OUTPUT -p tcp --dport 8080 -j REJECT 到 preStop,确保退出前零新请求流入。
健康检查与熔断的协同设计
当 Hystrix 熔断器开启时,/health/ready 应主动降级为 DOWN,避免 Kubernetes 将流量导向已熔断实例。某风控服务通过 @HealthIndicator 实现动态判断:
public Health health() {
if (circuitBreaker.isOpen()) {
return Health.down()
.withDetail("reason", "circuit breaker open")
.build();
}
return Health.up().build();
} 