原因
你的服务部署在不同的机房、不同的机器上,监听不同的端口,需要知道具体的定位信息。
服务上线
服务端启动的时候,需要往注册中心里注册自身的信息,主要是定位信息。
注册成功之后,注册中心和服务端要保持心跳。
客户端第一次发起对某个服务的调用之前,要先找注册中心获得所有可用服务节点列表,随后客户端会在本地缓存每个服务对应的可用节点列表。
客户端和注册中心要保持心跳和数据同步,后续服务端有任何变动,注册中心都会通知客户端,客户端会更新本地的可用节点列表。
客户端发送请求。
服务端返回响应。
服务下线
服务端通知注册中心自己准备下线了。
注册中心通知客户端某个服务端下线了。
客户端收到通知之后,新来的请求就不会再给该服务端发过去。
服务端等待一段时间之后,暂停服务并下线。
服务端崩溃检测
注册中心在没收到服务端的心跳,就要立刻通知客户端该服务端已经不可用了,那么客户端就不会再发请求过来。
注册中心还要继续往服务端发心跳,比如重试三次,而且重试间隔是十秒钟,发了一段时间之后发现心跳还是失败就不再发了,这意味着注册中心认定服务端彻底崩溃了。在彻底崩溃的场景下,注册中心不需要再次通知客户端,因为在之前注册中心就已经通知过了。
客户端容错
服务端崩溃的容错
在服务端节点崩溃之后,到注册中心发现,再到客户端收到通知,是存在一段延时的,这个延时是能算出来的。在这段延时内,客户端发送请求给这个服务端节点都会失败。这个时候需要客户端来做一些容错。
一般的策略是客户端在发现调不通之后,应该尝试换另外一个节点进行重试。如果客户端上的服务发现组件或者负载均衡器能够根据调用结果来做一些容错的话,那么它们应该要尝试将这个节点挪出可用节点列表,在短时间内不要再使用这个节点了。后面再考虑将这个节点挪回去。
如果注册中心最终发现服务端崩溃,然后通知了客户端,那么客户端就不用放回去了。等到注册中心发现服务端再次恢复了,那么注册中心会通知客户端,此时客户端更新可用节点列表就可以了。
客户端和服务端间不通的容错
但是有一种情况是需要客户端主动检测的。这种情况就是服务端节点还活着,注册中心也还活着,唯独客户端和服务端之间的网络有问题,导致客户端调用不通。
在这种情况下,类似于注册中心和服务端心跳失败,客户端也要朝着那个疑似崩溃的服务端节点继续发送心跳。如果心跳成功了,就将节点放回可用列表。如果连续几次心跳都没有成功,那么就不用放回去了,直接认为这个节点已经崩溃了。
客户端和注册中心不通的容错
这个分析也适用于客户端和注册中心心跳失败的场景。很显然在这种情况下,客户端可以直接使用本地缓存的可用节点列表,而后如果调不通了则处理方式完全一样。但是不同的是,如果客户端长期连不上注册中心,那么客户端本身应该考虑整个退出。
一个注册中心出故障之后你排查和后续优化的案例
案例背景:某天某公司的某个用Go语言编写的微服务项目突然出现了大量的服务调用超时和错误。通过查看日志和监控信息,初步怀疑是注册中心存在故障,导致服务之间无法正常调用。
-
故障排查
1.1 查看各个微服务的日志,发现服务间调用时出现大量找不到服务提供者的错误信息,初步判断可能是由于注册中心故障导致。
1.2 查看注册中心的状态和日志信息,发现注册中心CPU使用率持续100%,内存占用率也较高。
1.3 查看注册中心的GC(垃圾回收)情况,发现频繁进行Full GC,导致注册中心频繁处于停顿状态,无法正常提供服务。
1.4 查看注册中心的注册服务实例数量,发现注册服务实例数量远超预期,推测可能是服务注册重复或者服务未正确下线导致。
-
故障处理
2.1 优化注册中心的JVM参数,增加堆内存大小,调整新生代、老年代比例,优化GC参数,尽量减少Full GC的频率和影响。
2.2 清理注册中心的无效服务实例,通过查看各服务的健康状态和实际运行情况,手动剔除僵尸实例。
2.3 通知各服务团队检查服务注册和注销逻辑,确保服务在启动时正确注册,关闭时正确注销。
2.4 重启注册中心,恢复服务正常运行。
-
后续优化
3.1 升级注册中心的硬件资源,提高注册中心的处理能力。
3.2 对注册中心进行集群化部署,提高注册中心的高可用性。
3.3 对注册中心实施监控预警,通过监控CPU、内存、GC、服务实例数量等关键指标,实时关注注册中心的运行状态,并在发现异常时及时报警。
3.4 设置服务实例的有效期,对于长时间未发送心跳的服务实例,自动剔除,防止僵尸实例影响注册中心运行。
3.5 定期对注册中心进行压力测试和容量规划,确保注册中心能够承载业务的发展。
3.6 对Go微服务进行优化,提高服务的健壮性,例如增加熔断、降级和重试等机制,降低注册中心故障对业务的影响。