本文详细介绍了Sentinel与Feign的熔断学习入门,涵盖了Sentinel和Feign的基本概念和主要功能,并阐述了如何在Spring Boot项目中实现Sentinel与Feign的集成,包括环境搭建、熔断功能的实现以及实例项目的搭建。
Sentinel与Feign简介 Sentinel基础概念Sentinel是阿里巴巴开源的一款轻量级、高性能的流量控制组件,能够提供流量控制、熔断降级、系统保护等功能,有效防止系统在流量高峰时出现雪崩效应。
主要功能
- 流量控制:根据业务规则限制进入系统的流量。
- 熔断降级:当系统服务出现故障时,快速返回错误信息,避免连锁故障。
- 系统保护:实时监控系统负载,并根据负载情况自动调整流量,保证系统的稳定运行。
- 热点防护:对热点数据进行流量控制,防止因热点数据访问过频繁导致系统崩溃。
核心概念
- 资源:Sentinel中的“资源”可以是一个接口、一个方法,或者更多的抽象定义,代表系统中可监控的单元。
- 规则:规则定义了对资源的保护策略,例如QPS限制、响应时间控制等。
- 流控:当资源的访问量达到一定阈值时,自动减少流量,避免过载。
- 降级:在系统故障时,自动切换到备用策略,保证系统可用性。
- 系统保护:监控系统自身健康状态,如CPU、负载等。
Feign是一个声明式的Web服务客户端,使得写Web服务客户端变得更加容易。Feign提供了简单的方法来定义HTTP客户端,结合了Ribbon和Eureka的负载均衡功能,以及Hystrix的熔断降级功能。
主要特性
- 声明式API:通过注解定义HTTP请求,简化客户端开发。
- 负载均衡:Feign集成了Ribbon,实现负载均衡。
- 熔断降级:Feign集成了Hystrix,实现服务的熔断和降级。
- 异步支持:支持异步调用,提高系统响应速度。
核心概念
- 客户端:Feign客户端是通过注解定义的接口,映射HTTP请求。
- HTTP请求:通过注解定义HTTP请求的方法,如GET、POST、PUT等。
- 负载均衡:Feign客户端自动实现负载均衡。
- 熔断降级:当服务不可用时,Feign客户端立即返回默认的错误信息,而不是长时间等待。
- 异步调用:Feign支持异步调用,提高系统响应速度。
创建项目
- 创建一个新的Spring Boot项目,例如,使用Spring Initializr创建。
- 选择Spring Boot版本,选择Web和Actuator作为依赖。
项目结构
sentinel-feign-demo
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── sentinelfeign
│ │ │ ├── SentinelFeignApplication.java
│ │ │ ├── controller
│ │ │ │ └── HelloController.java
│ │ │ └── service
│ │ │ └── HelloService.java
│ │ └── resources
│ │ ├── application.yml
│ │ └── sentinel-feign-demo.yml
└── pom.xml
Maven依赖
在pom.xml
中添加必要的依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Spring Boot应用启动类
创建SentinelFeignApplication.java
,作为Spring Boot应用的启动类:
package com.example.sentinelfeign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.alibaba.sentinel.EnableSentinelWebAutoConfiguration;
@SpringBootApplication
@EnableFeignClients
@EnableSentinelWebAutoConfiguration
public class SentinelFeignApplication {
public static void main(String[] args) {
SpringApplication.run(SentinelFeignApplication.class, args);
}
}
添加Sentinel与Feign依赖
在上面的pom.xml
文件中已经添加了Sentinel和Feign的依赖。
Sentinel依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
Feign依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Feign熔断功能实现
Feign熔断的基本原理
Feign的熔断功能通过Hystrix实现。当服务不可用时,Feign客户端会立即返回一个默认的错误信息,而不是长时间等待。
主要步骤
- 服务调用:Feign客户端发起服务调用。
- 超时检测:如果服务调用超时,进入熔断降级流程。
- 熔断器状态转换:Hystrix会判断当前熔断器状态(开启、半开、关闭)。如果在熔断状态,直接返回熔断逻辑的结果;如果在半开状态,则尝试调用实际服务;如果在关闭状态,则正常调用。
- 熔断逻辑:如果服务调用失败,Hystrix会记录错误,并触发熔断器状态转换,进入熔断状态。
- 熔断恢复:在熔断状态下,Hystrix会定期尝试恢复调用,如果连续成功调用,熔断器会从熔断状态转换到半开状态,然后尝试调用实际服务,如果再次失败,熔断器会回到熔断状态。
服务端代码
在服务端实现一个简单的接口,提供sayHello
方法:
package com.example.sentinelfeign.service;
import org.springframework.stereotype.Service;
@Service
public class HelloService {
public String sayHello(String name) {
if ("error".equals(name)) {
throw new RuntimeException("Service error");
}
return "Hello, " + name;
}
}
Feign客户端代码
在客户端定义一个Feign客户端,调用服务端的sayHello
方法:
package com.example.sentinelfeign.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "hello-service")
public interface HelloClient {
@GetMapping("/hello")
String sayHello(@RequestParam("name") String name);
}
控制器代码
创建一个控制器来调用Feign客户端:
package com.example.sentinelfeign.controller;
import com.example.sentinelfeign.service.HelloClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
private final HelloClient helloClient;
public HelloController(HelloClient helloClient) {
this.helloClient = helloClient;
}
@GetMapping("/hello")
public String sayHello(@RequestParam("name") String name) {
return helloClient.sayHello(name);
}
}
测试熔断功能
为了演示熔断功能,可以将服务端的sayHello
方法改为抛出异常:
public String sayHello(String name) {
if ("error".equals(name)) {
throw new RuntimeException("Service error");
}
return "Hello, " + name;
}
当客户端调用sayHello
方法并传入name
参数为error
时,服务端会抛出异常,触发熔断降级逻辑。
Sentinel可以通过自定义的过滤器来保护Feign客户端。通过在SentinelFeignApplication.java
中添加自定义的过滤器,可以在请求进入和离开Feign客户端时进行流量控制和熔断保护。
自定义过滤器
package com.example.sentinelfeign;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.datasource.PropertyManager;
import com.alibaba.csp.sentinel.datasource.callback.DataSourcePropertyListener;
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
import com.alibaba.csp.sentinel.util.SentinelConfiguration;
import org.springframework.cloud.openfeign.FeignClientConfiguration;
import org.springframework.cloud.openfeign.support.FeignClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class SentinelFeignConfiguration {
@Bean
public DataSourcePropertyListener dataSourcePropertyListener() {
return new DataSourcePropertyListener() {
@Override
public void onRefresh(String origin) {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("hello-service");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(10);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
};
}
@Bean
public SentinelConfiguration sentinelConfiguration() {
TransportConfig.init();
PropertyManager.addListener(dataSourcePropertyListener());
return new SentinelConfiguration();
}
}
配置Sentinel规则以实现熔断机制
通过DataSourcePropertyListener
类可以监听Sentinel规则的变化,当规则发生变化时,可以动态更新Sentinel的规则:
@Bean
public DataSourcePropertyListener dataSourcePropertyListener() {
return new DataSourcePropertyListener() {
@Override
public void onRefresh(String origin) {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("hello-service");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(10);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
};
}
实战案例解析
示例项目搭建
服务端代码
服务端提供一个简单的HelloService
接口:
package com.example.sentinelfeign.service;
import org.springframework.stereotype.Service;
@Service
public class HelloService {
public String sayHello(String name) {
if ("error".equals(name)) {
throw new RuntimeException("Service error");
}
return "Hello, " + name;
}
}
Feign客户端代码
定义一个Feign客户端,调用服务端的sayHello
方法:
package com.example.sentinelfeign.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "hello-service")
public interface HelloClient {
@GetMapping("/hello")
String sayHello(@RequestParam("name") String name);
}
控制器代码
创建一个控制器来调用Feign客户端:
package com.example.sentinelfeign.controller;
import com.example.sentinelfeign.service.HelloClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
private final HelloClient helloClient;
public HelloController(HelloClient helloClient) {
this.helloClient = helloClient;
}
@GetMapping("/hello")
public String sayHello(@RequestParam("name") String name) {
return helloClient.sayHello(name);
}
}
配置文件
在application.yml
中配置服务端:
server:
port: 8080
在sentinel-feign-demo.yml
中配置Sentinel规则:
sentinel:
flow:
rules:
- resource: hello-service
grade: QPS
count: 10
meterIntervalSec: 1
模拟高并发场景测试熔断功能
高并发场景模拟
使用工具如Apache JMeter或LoadRunner模拟高并发场景。
- 配置JMeter:在JMeter中配置HTTP请求,调用
http://localhost:8080/hello?name=test
。 - 设置并发数:设置并发用户数为100,每个用户发送100次请求。
- 启动负载测试:启动JMeter,开始进行负载测试。
观察熔断效果
在高并发场景下,由于设置了Sentinel的QPS限制为10,当请求量超过10时,Sentinel会触发熔断降级逻辑,返回默认的错误信息。
故障注入
为了进一步测试熔断降级功能,可以在服务端代码中注入故障:
public String sayHello(String name) {
if ("error".equals(name)) {
throw new RuntimeException("Service error");
}
return "Hello, " + name;
}
当客户端调用sayHello
方法并传入name
参数为error
时,服务端会抛出异常,触发熔断降级逻辑。
- Feign服务调用失败
- 错误:Feign客户端调用服务时失败。
- 原因:服务端接口定义不正确,或服务端未启动。
- 解决方案:检查服务端接口定义是否正确,确保服务端已启动并正常运行。
- 熔断器状态转换失败
- 错误:熔断器状态转换失败。
- 原因:熔断器配置不正确,或服务端调用失败。
- 解决方案:检查熔断器配置是否正确,确保服务端调用正常。
- Sentinel规则加载失败
- 错误:Sentinel规则加载失败。
- 原因:Sentinel规则配置文件错误,或Sentinel未启动。
- 解决方案:检查Sentinel规则配置文件是否正确,确保Sentinel已启动。
- 熔断降级逻辑未触发
- 错误:熔断降级逻辑未触发。
- 原因:熔断器状态未转换到熔断状态。
- 解决方案:检查熔断器状态是否正确,确保服务端调用失败。
- 监控服务调用成功率
- 监控指标:服务调用成功率。
- 监控工具:使用Prometheus和Grafana监控服务调用成功率。
- 监控熔断器状态
- 监控指标:熔断器状态。
- 监控工具:使用Prometheus和Grafana监控熔断器状态。
- 监控服务端性能
- 监控指标:服务端CPU使用率、内存使用率、响应时间等。
- 监控工具:使用Prometheus和Grafana监控服务端性能。
- 监控Sentinel规则变化
- 监控指标:Sentinel规则变化。
- 监控工具:使用Prometheus和Grafana监控Sentinel规则变化。
通过以上监控建议,可以全面监控系统运行状态,及时发现并解决问题,确保系统稳定运行。