一、Eureka ------ 服务注册与发现
1.1 什么是服务注册与发现
Spring Cloud封装了Netflix开发的Eureka模块来实现服务治理。在微服务架构中,服务实例的IP和端口是动态变化的,传统静态配置方式完全失效。服务注册发现机制通过三大核心能力解决这一挑战:动态感知(新实例自动注册、故障实例自动剔除)、位置透明(服务消费者无需关注提供者物理位置)、负载均衡(结合客户端负载均衡算法智能分配请求流量)。
Eureka采用C/S架构设计,Eureka Server作为服务注册功能的服务器(即服务注册中心),系统中的其他微服务使用Eureka客户端连接到Eureka Server并维持心跳连接。
1.2 核心工作机制
-
服务注册:各个微服务节点启动后,会在Eureka Server中进行注册,Eureka Server的服务注册表中存储所有可用服务节点的信息。
-
心跳续约:Eureka客户端在应用启动后向Eureka Server发送心跳,默认周期为30秒。
-
服务剔除:如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳(默认90秒),会将该服务节点从注册表中移除。
-
自我保护机制:当网络异常时自动进入保护状态,避免因心跳超时误删健康实例。当Eureka Server检测到大量服务实例失去联系时,不会立即移除它们,而是保留注册信息,防止误判。
1.3 关键配置
服务端配置(Eureka Server):
eureka:
server:
enable-self-preservation: false # 关闭自我保护模式(生产环境建议开启)
eviction-interval-timer-in-ms: 5000 # 清理间隔
客户端配置(Eureka Client):
eureka:
instance:
lease-renewal-interval-in-seconds: 15 # 心跳间隔
client:
service-url:
defaultZone: http://peer1:8761/eureka/,http://peer2:8761/eureka/
register-with-eureka: true
fetch-registry: true
依赖引入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
服务端启动类添加 @EnableEurekaServer 注解。
客户端启动类添加@EnableDiscoveryClient
1.4 高可用部署
Eureka Server采用Peer-to-Peer对等架构,推荐至少部署3个节点构成高可用集群。多个Eureka Server之间相互注册,实现注册表数据同步,防止单点故障。
二、Ribbon ------ 多实例负载均衡
2.1 什么是Ribbon
Ribbon是Netflix开源的项目,主要功能是提供客户端的软件负载均衡算法和服务调用。与Nginx这种服务端负载均衡不同,Ribbon属于进程内负载均衡------将负载均衡逻辑集成到消费方,消费方从服务注册中心获知可用地址,然后自己选择合适服务器。Ribbon在工作时分两步:先选择Eureka Server(优先选择同一区域内负载较少的server),再根据用户指定的策略从服务注册列表中选择一个地址。
2.2 内置负载均衡策略
Ribbon提供了多种内置负载均衡策略:
| 策略 | 说明 | 适用场景 |
|---|---|---|
| 轮询(RoundRobinRule) | 按顺序循环分配请求 | 实例性能均等的场景 |
| 随机(RandomRule) | 随机选择服务实例 | 避免热点问题 |
| 重试(RetryRule) | 在指定时间内重试失败请求 | 需要自动恢复的场景 |
| 区域感知(ZoneAvoidanceRule) | 结合实例所在区域调度,优先同区域 | 降低网络延迟 |
| 响应时间加权(WeightedResponseTimeRule) | 根据响应时间动态调整权重 | 自动规避高延迟实例 |
2.3 关键配置
order-service: # 针对特定服务配置
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
ConnectTimeout: 500 # 连接超时(默认1000ms)
ReadTimeout: 3000 # 读取超时(默认1000ms)
MaxAutoRetries: 1 # 同一实例重试次数
MaxAutoRetriesNextServer: 1 # 切换实例重试次数
2.4 使用方式
通过 @LoadBalanced 注解注入RestTemplate,Ribbon会自动拦截请求,根据服务ID从注册中心查找实例列表:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
三、Spring Cloud Gateway ------ 统一入口网关
3.1 什么是Gateway
Spring Cloud Gateway是Spring Cloud生态中的新一代API网关组件,基于Spring Framework 5、Project Reactor和Spring Boot构建,采用响应式编程模型,底层使用Netty作为通信框架。相比Zuul 1.x,Gateway的吞吐量可达其3-5倍,平均延迟降低60%以上。
API网关作为所有外部请求的统一入口,承担着路由转发、安全认证、流量控制、监控日志等关键职责。
3.2 三大核心概念
Gateway引入了路由(Route)、断言(Predicate)和过滤器(Filter)三个核心概念:
-
路由(Route) :网关的基本构建块,包含目标URI、断言集合和过滤器链三个关键要素。
-
断言(Predicate) :匹配条件,支持Path、Header、Cookie等20+种路由条件。
-
过滤器(Filter) :分为前置过滤器(业务执行前)和后置过滤器(业务执行后),可实现鉴权、限流、日志等横切关注点。
3.3 基础路由配置
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service # lb:// 表示启用负载均衡
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1 # 移除首层路径前缀
-
id:路由唯一标识 -
uri:目标服务地址,lb://前缀表示启用负载均衡 -
predicates:断言条件集合 -
filters:过滤器链(按声明顺序执行)
3.4 与Eureka集成实现服务发现
Gateway可以配置为基于服务注册中心自动创建路由:
spring:
cloud:
gateway:
discovery:
locator:
enabled: true # 开启基于服务发现的路由定位
四、OpenFeign ------ 服务远程调用
4.1 什么是OpenFeign
OpenFeign是一个声明式的HTTP客户端框架,最初由Netflix开发,现已成为Spring Cloud生态的核心组件。它的核心价值在于将HTTP请求抽象为Java接口方法,通过注解驱动实现服务间通信。
声明式编程与命令式编程的本质区别:命令式编程需要详细描述"如何做"的每一个步骤(手动拼接URL、设置请求头、解析响应等),而声明式编程只关注"做什么",底层通信由框架自动处理。
4.2 基本用法
@FeignClient(name = "order-service")
public interface OrderClient {
@GetMapping("/orders/{id}")
Order getOrderById(@PathVariable("id") Long id);
@PostMapping("/orders")
Order createOrder(@RequestBody OrderDTO orderDTO);
}
当配置url属性时,OpenFeign直接使用固定地址;若省略该属性,则通过服务名从注册中心获取可用实例列表。
4.3 负载均衡集成
OpenFeign的负载均衡能力通过与Spring Cloud LoadBalancer集成实现。实例选择流程:
-
从注册中心拉取服务实例元数据(IP、端口、健康状态)
-
根据配置的负载均衡策略(默认轮询)选择目标实例
-
动态构建请求URL
4.4 关键配置
feign:
client:
config:
default:
connectTimeout: 5000 # 连接超时
readTimeout: 3000 # 读取超时
loadbalancer:
strategy: roundRobin # 轮询(默认),可选random、retry等
4.5 编码优势对比
| 特性 | OpenFeign | RestTemplate |
|---|---|---|
| 代码量 | 接口定义+注解(5-10行) | 手动构造请求(20-30行) |
| 可维护性 | 接口变更自动适配 | 需同步修改调用代码 |
| 测试友好性 | 可直接mock接口 | 需模拟HTTP响应 |
五、Resilience4j ------ 服务熔断与降级
5.1 什么是Resilience4j
Resilience4j是Hystrix的替代方案,是一个轻量级的服务容错框架,具有"原生整合Spring Boot、无多余依赖、功能全面、配置灵活"的优势。核心提供重试、熔断、降级、限流、舱壁五大能力。
在分布式体系中,服务间的依赖关系可能导致雪崩效应------某个服务响应过慢或不可用,导致上游服务占用越来越多资源,最终整个系统崩溃。断路器的作用就是当故障发生时"跳闸",避免故障蔓延。
5.2 断路器(Circuit Breaker)状态机
断路器有三种状态:
-
Closed(关闭) :正常状态,请求正常通过
-
Open(打开) :熔断触发,直接返回降级结果,不发起实际调用
-
Half-Open(半开) :经过冷却时间后,允许部分试探请求,检测服务是否恢复
5.3 熔断与降级配置
spring:
cloud:
circuitbreaker:
resilience4j:
circuitbreaker:
configs:
default:
failureRateThreshold: 50 # 故障率阈值50%
waitDurationInOpenState: 5s # 断路器打开持续时间
slidingWindowType: COUNT_BASED # 滑动窗口类型
slidingWindowSize: 100 # 滑动窗口大小
minimumNumberOfCalls: 20 # 计算故障率前最少调用次数
permittedNumberOfCallsInHalfOpenState: 10 # 半开状态允许的调用数
automaticTransitionFromOpenToHalfOpenEnabled: true
5.4 限流(Rate Limiter)配置
resilience4j:
ratelimiter:
configs:
default:
limitForPeriod: 10 # 每个时间段允许的最大请求数
limitRefreshPeriod: 1s # 刷新时间周期
timeoutDuration: 0s # 获取许可证的等待时间(0s表示不等待)
5.5 代码中使用
@Service
public class MyService {
@CircuitBreaker(name = "myService", fallbackMethod = "fallback")
public String doSomething() {
// 业务逻辑
}
public String fallback(Throwable t) {
return "服务繁忙,请稍后重试"; // 降级返回友好提示
}
}
通过 @CircuitBreaker、@Retry、@RateLimiter、@TimeLimiter、@Bulkhead 等注解将容错策略应用到方法上,并通过fallbackMethod指定降级逻辑。
六、组件协作全景图
一个完整的微服务请求链路如下:
外部请求 → Spring Cloud Gateway(路由+限流)
↓
服务消费者(OpenFeign声明式调用 + Ribbon负载均衡)
↓
服务提供者(注册到Eureka)
↓
Resilience4j(熔断/降级保护)
各组件职责分工:
-
Eureka:服务注册中心,管理所有服务实例的注册信息
-
Ribbon:客户端负载均衡,在服务调用时选择合适的实例
-
Gateway:统一入口,路由转发、限流、鉴权
-
OpenFeign:声明式远程调用,简化服务间HTTP通信
-
Resilience4j:服务容错,熔断、降级、限流保护
Gateway与Resilience4j组合是主流方案,同时建议在服务层也做本地熔断,避免网关层面成为瓶颈。
所遇到的问题
1、 项目依赖与版本兼容
现象
-
启动时出现
GatewayClassPathWarningAutoConfiguration或spring-cloud-starter-gateway is deprecated警告。 -
IDE 中某些类(如
RateLimitException)无法导入,显示红色。
原因
Spring Cloud Gateway 从 4.x 起(对应 Spring Boot 3.x)重构了模块,将核心内容拆分为 spring-cloud-gateway-server-webflux 和 spring-cloud-starter-gateway-server-webflux。旧模块已被弃用,将在下一个大版本移除。
解决方案(正确依赖配置)
在父 POM 或子模块的 pom.xml 中,只保留新依赖,不要同时引入新旧两个:
<!-- 推荐:只引入这一个 Starter -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-server-webflux</artifactId>
</dependency>
<!-- 如果需要 Eureka 客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 负载均衡(Gateway 默认依赖) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- Redis Reactive(限流必须) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
2、Gateway 配置迁移(弃用警告)
现象
Property source 'application.yaml':
Key: spring.cloud.gateway.routes[0].id → Replacement: spring.cloud.gateway.server.webflux.routes[0].id
...
原因
配置属性的根路径从 spring.cloud.gateway 迁移到 spring.cloud.gateway.server.webflux。
解决方案(正确配置格式)
yaml
spring:
cloud:
gateway:
server:
webflux:
routes:
- id: user-service
uri: lb://USER-SERVICE # 使用服务名进行负载均衡
predicates:
- Path=/api/user/**
filters:
- name: RequestRateLimiter
args:
key-resolver: "#{@ipKeyResolver}"
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
注意 :缩进必须正确,routes 与 webflux 对齐(2 空格),列表项缩进 2 空格。
3、LoadBalancer下划线异常
现象
该异常是由于 Spring Cloud 的 LoadBalancerInterceptor 在解析请求 URI 时,通过 URI.getHost() 获取服务名,但得到的值为 null,从而触发了断言失败。
原因
-
URI
http://USER53558_CLIENT2/hello中的主机名USER53558_CLIENT2包含下划线_。 -
尽管
java.net.URI在较新版本(Java 8+)中通常允许下划线,但在某些 Java 版本或特定的运行环境中,URI.getHost()可能因为主机名不符合 DNS 命名规范而返回null(尤其当URI的parseServerAuthority()被隐式或显式调用时)。 -
Spring Cloud 的
LoadBalancerInterceptor直接使用URI.getHost()来提取服务名,当结果为null时就会抛出此异常。
4、Jackson 反序列化失败(无法构造实例)
现象
feign.codec.DecodeException: Type definition error: [simple type, class com.example.user53558_test.domain.entiry.User]
...
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Cannot construct instance of `com.example.user53558_test.domain.entiry.User`
(no Creators, like default constructor, exist): cannot deserialize from Object value
原因
-
Feign 默认使用 Jackson 将远程服务返回的 JSON 反序列化为 Java 对象。
-
Jackson 在反序列化时,需要能够创建目标类的实例。
-
默认情况下,Jackson 通过 无参构造方法(public 或 protected)来创建对象,然后通过 setter 或字段赋值。
-
如果类中没有无参构造,且未提供
@JsonCreator标注的带参构造,则抛出上述异常。
5、Resilience4j Fallback 方法匹配失败
现象
WARN ... FallbackExecutor : No fallback method match found
java.lang.NoSuchMethodException: interface java.util.List
class com.example.user53558_test.Service.impl.imageserviceimpl.fallbackHandler(int,class java.lang.Throwable)
原因
-
在
imageserviceimpl中使用了@CircuitBreaker注解并指定了fallbackMethod = "fallbackHandler"。 -
Resilience4j 在触发熔断或异常时,通过反射调用 fallback 方法。
-
反射要求方法签名(方法名、参数类型、返回值)完全匹配。
-
错误提示
NoSuchMethodException,说明找不到符合签名fallbackHandler(int, Throwable)的方法,返回值类型期望为List。
解决方式:
-
Resilience4j 的 fallback 方法必须与原始方法在同一类 中,且为 public。
-
参数顺序:先原方法的全部参数,再可选的
Throwable(可放在最后)。 -
如果方法返回
List,fallback 也必须返回List,不能返回Object或其他类型。