前言
- 故障转移策略是保障系统高可用性的核心机制,旨在服务实例故障时自动切换流量,避免单点故障引发系统级联崩溃。
核心故障转移策略
- 故障切换(Failover)
- 原理:当调用某服务实例失败时,自动切换到其他健康实例重试。
- 适用场景:幂等性接口(如查询类服务)。
- 实现方式:
- 服务发现(如Nacos、Eureka)动态维护可用实例列表。
- 客户端负载均衡(如Ribbon、Spring Cloud LoadBalancer)选择备用节点。
- 快速失败(Failfast)
- 原理:立即返回错误,避免因重试导致资源阻塞。
- 适用场景:非幂等操作(如支付扣款)。
- 实现方式:配置超时时间(如Feign的readTimeout),超时后直接抛出异常。
- 故障静默(Failsilent)
- 原理:标记故障节点为“不可用”,暂时停止向其发送请求。
- 适用场景:频繁超时或宕机的服务。
- 实现方式:结合熔断器(如Hystrix、Sentinel)实现节点熔断。
- 并行调用(Forking)
- 原理:同时向多个服务副本发送请求,取最先响应的结果。
- 适用场景:对延迟敏感的关键服务(如实时风控)。
- 实现方式:异步线程池(如CompletableFuture)并发调用。
关键技术实现
- 服务发现与健康检查
- 作用:动态感知服务实例状态,剔除故障节点。
- 工具:Consul、Nacos、Kubernetes的Endpoint Controller。
- 健康检查方式:
- 主动探测:定期HTTP/TCP探测。
- 心跳上报:客户端定时发送心跳包。
- 熔断器(Circuit Breaker)
- 原理:统计失败请求比例,触发熔断后直接拒绝流量。
- 实现:
- Hystrix:通过@HystrixCommand配置熔断阈值。
- Sentinel:基于QPS、响应时间动态熔断。
- 负载均衡策略
- 策略类型:
- 轮询(Round Robin):均匀分配请求。
- 权重(Weighted):根据实例性能分配权重。
- 最少连接(Least Connections):优先选择负载低的节点。
- 工具:Ribbon、Spring Cloud Gateway、Nginx。
- 重试机制(Retry)
- 配置要点:
- 重试次数(避免无限重试导致雪崩)。
- 退避策略(如指数退避,避免集中重试)。
- 实现:Spring Retry、Resilience4j。
- 服务降级(Degradation)
- 策略:
- 默认返回值:返回缓存数据或静态兜底值。
- 功能降级:关闭非核心功能(如关闭推荐服务,保留购物车)。
- 工具:Hystrix Fallback、Sentinel降级规则。
实践理解
package com.xiaohan.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.util.List;
@RestController
@Slf4j
public class FailOver {
@Autowired
private DiscoveryClient discoveryClient;
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/failOver")
public String failOver() {
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("Member-Service");
for (ServiceInstance serviceInstance : serviceInstances) {
URI uri = serviceInstance.getUri();
String targetUrl = uri + "/getMember";
try {
return "订单服务调用会员服务接口:" + restTemplate.getForObject(targetUrl, String.class);
} catch (RestClientException e) {
log.error("RPC远程调用故障:{}", e.getMessage());
}
}
return "fail";
}
}