一、故障的真实场景与初步排查

最近公司做年度促销的秒杀活动,当天下午三点,核心用户服务突然挂了一个节点,紧接着系统里的订单服务、商品服务开始批量报错,调用用户服务的接口全超时。运维同学第一时间排查监控,发现TSE注册中心的这个用户服务节点已经被标记为“下线”,剩下的两个节点也处于“不健康”状态,导致所有服务都找不到可用的用户服务实例,整个秒杀页面无法加载,影响了近千个用户的参与。后来翻了之前的配置,发现有两个之前没注意到的小坑,就是心跳和健康检查的设置,这次把这些坑拆解清楚,帮大家避免踩类似的雷。

1.1 为什么注册中心能管所有服务的死活?

用生活化的例子说,你平时点外卖时,平台会把所有商家列出来,这个商家列表就相当于微服务里的注册中心,每个入驻的商家(对应微服务的节点)要定期给平台发消息说“我还开门营业着”,这个消息就是心跳。如果商家超过一定时间没发消息,平台就会把它从列表里删掉,客人(其他服务)就找不到这个商家了,自然没法下单(调用接口)。

1.2 这次故障的具体排查过程

当时先看注册中心的节点状态,发现用户服务的三个节点里,一个已经崩了,剩下的两个节点的心跳都超时了,注册中心以为整个用户服务都挂了,所以把它从服务列表里清掉。再查配置,发现心跳间隔设的是10秒,超时时间是5秒——也就是说,节点刚发完心跳,注册中心过5秒就没收到下一个,就判定节点故障,相当于老板让员工每天打卡,员工10天打一次,老板5天没看到打卡就把员工开除了,这显然不合理。而且当时注册中心是单节点部署,整个注册中心挂了就全完了。

二、拆解心跳机制的常见配置陷阱

2.1 心跳的核心作用

简单说就是“报平安”,每个微服务节点会按固定时间间隔给注册中心发请求,告诉对方“我活着,能正常提供服务”,注册中心收到后会更新这个节点的状态。如果连续几次没收到,就会把这个节点标记为不可用,其他服务就不会再调用它了。

2.2 陷阱1:心跳间隔和超时时间设置不合理

刚才的故障里,间隔和超时设置得太紧凑,容易因为网络波动误判节点故障。那正确的配置应该是多少?根据不同场景调整,日常业务可以设间隔5秒,超时15秒——因为网络偶尔会有延迟,给节点留够缓冲时间,不会轻易踢下线。这里给出适配Spring Cloud Alibaba技术栈(与TSE注册中心兼容)的配置示例:

# 采用Spring Cloud Alibaba + Nacos作为微服务技术栈
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 你的TSE注册中心地址:8848
        # 心跳间隔:每5秒发送一次心跳,平衡实时性和网络消耗
        heart-beat-interval: 5000
        # 心跳超时时间:连续3次未收到心跳,判定节点故障(5秒×3=15秒)
        heart-beat-timeout: 15000
        # 健康检查类型选HTTP,比TCP更能确认服务内部正常
        health-check-type: HTTP
        # 健康检查路径:Spring Boot Actuator自动生成的健康接口,确保服务真的可用
        health-check-path: /actuator/health

这个配置的核心是给网络波动留了缓冲空间,避免误判,之前的错误配置就是把超时设成了5秒,太严格了。

2.3 陷阱2:注册中心用单节点,不是集群

刚才的故障里,TSE注册中心是单节点,那个节点刚好出了点小问题,虽然只是暂时的,但导致整个注册中心的服务发现都失效了。就像外卖平台的服务器只有一台,服务器断网了,所有商家和顾客都没法用了。所以注册中心一定要用集群部署,TSE注册中心本身支持3节点或5节点集群,服务配置的时候要把所有节点的地址都写上,这样即使一个节点挂了,还有其他节点可用。示例配置如下:

// TSE注册中心集群配置,保证高可用
{
  "spring": {
    "cloud": {
      "nacos": {
        "discovery": {
          "server-addr": "tse-node1:8848,tse-node2:8848,tse-node3:8848"
        }
      }
    }
  }
}

这里的配置把三个节点的地址都列出来,服务会自动选择可用的节点,不会因为单个节点故障导致注册中心不可用。

三、健康检查的配置坑点与规避方法

3.1 健康检查和心跳的区别

心跳只是检查节点有没有活着(进程在不在),但健康检查是检查服务内部是不是真的能正常提供功能——比如进程在,但核心业务线程挂了,心跳会正常发,但健康检查会返回失败。就像商家开着门,但厨房着火了,没法做饭,客人来了也没法下单,健康检查就是看厨房能不能正常做饭。

3.2 常见健康检查陷阱:路径写错

很多开发者会随便写健康路径,比如把路径写成/health,而Spring Boot Actuator的默认路径是/actuator/health,如果配置的时候写错,注册中心每次检查都会返回404或者500,就会判定节点不健康,即使节点是好的。之前就有个同事犯过这个错,把health-check-path写成/health,导致整个服务被下线,排查了半天才发现是路径写错了。

3.3 其他配置注意事项

比如健康检查的超时时间,不要设太小,比如设成1秒,服务处理健康接口需要时间,容易超时,设成3秒比较合适。另外,不要随便关闭健康检查,虽然会减少一点网络消耗,但会导致注册中心不知道服务内部的状态,容易出问题。

3.4 不同场景的配置选择

对于秒杀、支付这类高可用要求的场景,心跳间隔设3秒,超时设8秒,健康检查路径用Actuator的接口,检查类型选HTTP;对于邮件通知、日志采集这类低频服务,心跳间隔可以设15秒,超时设45秒,减少不必要的网络请求。

四、总结

这次故障给我们的教训是,微服务的注册中心配置里,心跳和健康检查的细节非常重要,不能用默认值就不管了。尤其是用TSE这类兼容Nacos的注册中心时,要根据业务场景调整心跳和健康检查的参数,必须用注册中心集群,不能单节点。平时要多监控注册中心的心跳成功率和健康检查的状态,发现超时要及时排查,不要等线上出问题才补救。很多线上的大故障都是因为这些小配置的坑没注意到,只要把这些细节做好,就能避免很多服务发现失败的问题。