2. 医疗微服务注册与发现

在医疗微服务架构中,服务实例会随容器调度、灰度发布、故障迁移而频繁变化。如果调用方仍依赖固定 IP 或配置文件中的地址列表,一次扩容或一次节点漂移就会造成大面积调用失败。注册与发现要解决的核心问题是:让服务实例的“当前可用地址”成为可被自动维护、自动感知、自动消费的动态数据。它由三部分构成:服务注册中心负责保存实例目录,健康检查负责判定实例是否可用,服务发现负责把可用实例列表交给调用方并支持多集群、多环境的隔离寻址。

一、注册中心选型:先看医疗场景的约束

医疗业务对注册中心的诉求与普通互联网业务不完全相同。第一,可用性优先级高,挂号、医嘱、检验申请等链路不允许因注册中心抖动导致全量调用失败;第二,网络环境复杂,院内常存在多机房、多网段、内外网隔离,注册中心需要支持跨集群同步或分区自治;第三,合规要求明确,服务目录、实例元数据、调用关系可能涉及审计,注册中心应支持鉴权、命名空间隔离和操作留痕;第四,技术栈可能混合,既有 Spring Cloud 体系,也有 Kubernetes 原生服务,还可能存在 Dubbo 或 gRPC 服务。

常见选型对象包括 Nacos、Consul、Eureka、ZooKeeper 以及 Kubernetes Service/DNS。Nacos 同时提供注册发现与配置管理,支持命名空间、分组、集群和临时/持久实例,适合多环境多集群的医疗平台;Consul 的强项是服务目录、健康检查与多数据中心,适合已有 Consul 基础设施或需要跨数据中心联邦的场景;Eureka 为 AP 模型,客户端缓存友好,但已停止新功能演进,适合存量 Spring Cloud 系统;ZooKeeper 为 CP 模型,注册中心并非其最自然的使用方式,网络分区时可能牺牲可用性;Kubernetes Service 适合集群内东西向流量,但跨集群、跨虚拟机、非 K8s 工作负载需要额外方案。选型时建议用一张决策表把“一致性模型、健康检查方式、多集群能力、命名空间隔离、鉴权审计、客户端生态、运维成本”逐项打分,而不是只看社区热度。

二、健康检查机制:区分“活着”和“能服务”

健康检查不是简单的端口探测。医疗服务常见三类状态:进程存活但数据库连接池耗尽、进程存活但依赖的 PACS 接口超时、进程存活但正在执行不可中断的批量任务。若只做 TCP 端口检查,这些实例仍会被注册中心标记为健康,流量继续打入,导致超时和雪崩。因此健康检查应分层设计。

第一层是存活检查,判断进程是否还在,通常由容器 liveness probe 或注册中心心跳完成。第二层是就绪检查,判断实例是否具备接收业务流量的条件,例如数据库连接可用、缓存预热完成、配置加载成功。第三层是业务健康检查,暴露 /health/biz 之类的端点,检查关键下游依赖和内部队列水位。注册中心侧通常采用心跳续约加服务端主动探测的组合:临时实例由客户端定期发送心跳,超时未续约则剔除;持久实例由注册中心主动发起 HTTP/TCP 探测。剔除策略要避免“抖动即下线”,可设置连续失败阈值、保护阈值和延迟剔除时间。例如心跳间隔 5 秒、超时 3 秒、连续 3 次失败后标记不健康,再经过 15 秒观察期才从可用列表移除。对于正在下线的实例,应先调用注销接口或发送下线信号,让调用方停止路由,再等待存量请求处理完毕,最后停止进程。

医疗微服务注册与发现:多集群多环境视图 命名空间隔离:prod-hospital-a / prod-hospital-b / staging / dev 集群 A(主院区 · prod-hospital-a) 注册中心 A Nacos 集群 · 3 节点 挂号服务实例 10.10.1.21:8080 临时实例 · 心跳续约 健康检查分层 L1 存活:进程/端口心跳 L2 就绪:DB连接池、配置加载 L3 业务:PACS/LIS 依赖、队列水位 调用方(门诊网关) 订阅服务名 → 本地缓存实例列表 负载均衡:加权轮询 + 故障摘除 推送变更:UDP/长连接 秒级生效 集群 B(分院区 · prod-hospital-b) 注册中心 B Nacos 集群 · 3 节点 挂号服务实例 10.20.1.31:8080 临时实例 · 心跳续约 跨集群同步策略 同环境:双向同步,就近优先 跨环境:禁止同步,命名空间硬隔离 故障:分区自治,恢复后增量合并 调用方(住院网关) 优先本集群实例,跨集群仅兜底 元数据路由:zone=hospital-b 超时熔断:跨集群 RTT 阈值 50ms 同环境跨集群同步 注册:实例启动上报元数据 → 心跳续约 → 健康检查判定 → 发现:订阅推送/定时拉取 → 本地缓存 → 负载均衡路由

三、多集群与多环境服务发现实践

多环境的核心是隔离,多集群的核心是就近与容灾。建议用“命名空间 + 分组 + 集群元数据”三层表达:命名空间对应 dev、staging、prod-hospital-a、prod-hospital-b,做到跨环境不可见;分组对应同一环境内的业务域,如 registration、lis、pacs;集群元数据用 cluster、zone、region 标记实例位置。调用方订阅时带上目标环境和集群偏好,注册中心只返回匹配的实例列表。

跨集群同步要区分场景。同一环境的两个院区集群,通常希望双向同步,让 A 区调用方在 B 区实例上也能兜底;但生产与测试之间必须禁止同步,否则测试流量可能打到生产库。同步方式可采用注册中心自带的集群同步、网关层聚合或独立的服务目录同步组件。同步延迟要可观测,建议暴露“同步落后条数”和“最后同步时间”指标。当跨集群网络中断时,应允许各集群分区自治,恢复后按版本号或时间戳做增量合并,避免旧数据覆盖新数据。

调用方的发现策略同样关键。推荐“本地缓存 + 订阅推送 + 定时兜底拉取”组合:启动时全量拉取,运行中接收变更推送,同时每 30 秒做一次兜底拉取,防止推送丢失。负载均衡优先选择同集群、同 zone 实例,跨集群实例仅作为兜底,并设置更短的超时和熔断阈值。对于挂号、缴费等强一致要求链路,可配置固定权重和最小可用实例数,避免全部流量集中到少数实例。

四、可直接参考的配置样例

以下为 Nacos 场景下的示例配置,参数为教学假设值,需按实际环境调整:

spring:
  cloud:
    nacos:
      discovery:
        server-addr: nacos-prod-hospital-a:8848
        namespace: prod-hospital-a
        group: REGISTRATION
        cluster-name: hospital-a
        ephemeral: true
        heart-beat-interval: 5000
        heart-beat-timeout: 15000
        ip-delete-timeout: 30000
        metadata:
          zone: east-1
          version: v2.3.1
          env: prod

健康检查端点示例:

GET /actuator/health/liveness   → {"status":"UP"}
GET /actuator/health/readiness  → {"status":"UP","db":"UP","redis":"UP"}
GET /health/biz                 → {"status":"UP","pacs":"UP","lis":"DEGRADED"}

当 /health/biz 返回 DEGRADED 时,注册中心可继续保留实例但降低权重,而不是直接剔除,这样既避免流量打入不可用实例,也保留恢复后的快速回归能力。判断是否达成目标,可观察三个信号:注册中心实例数与实际 Pod/进程数一致;调用方本地缓存中不健康实例占比低于 1%;跨集群调用比例在正常时段低于 5%,故障切换时能在 10 秒内完成路由调整。

医疗场景建议为注册中心配置鉴权与审计:开启命名空间级权限,记录实例注册、注销、健康状态变更事件,保留时间不少于 180 天,便于故障回溯与合规检查。
不要把生产与测试环境放在同一命名空间,也不要用同一套注册中心集群承载全部环境。环境隔离一旦被打破,测试实例可能被生产调用方发现并路由,造成数据污染。