实战Spring Cloud之流量控制组件Sentinel
原创
©著作权归作者所有:来自51CTO博客作者zhuhuix的原创作品,请联系作者获取转载授权,否则将追究法律责任
文章目录
背景
一、Sentinel是什么
- Sentinel是阿里巴巴旗下的开源流量控制组件,通过以下链接《Sentinel 介绍》可以对Sentinel有个简单了解。
- 本文将分别通过服务提供方及服务消费者两个角度说明Sentinel是如何进行流量控制和熔断降级的。
1.1 安装Sentinel控制台
# 下载服务端
wget https://github.com/alibaba/Sentinel/releases/download/v1.8.0/sentinel- dashboard-1.8.0.jar
# 启动服务(默认端口为8080)
- 登录控制台,默认用户名与密码为sentinel
二、创建微服务
2.1 创建微服务工程项目
- 通过Spring网站创建项目
- 项目命名
- 勾选Spring Web,Nacos Service Discovery,Sentinel依赖
- 检查pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>nacos.democonsumer</groupId>
<artifactId>sentinel</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sentinel</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2 向Nacos服务中心注册微服务
- 通过 Spring Cloud 原生注解 @EnableDiscoveryClient 开启服务注册发现功能
// 通过 Spring Cloud 原生注解 @EnableDiscoveryClient 开启服务注册发现功能
@EnableDiscoveryClient
@SpringBootApplication
public class SentinelApplication {
public static void main(String[] args) {
SpringApplication.run(SentinelApplication.class, args);
}
}
server:
port: 8083
spring:
application:
name: goods-service
cloud:
# nacos服务注册
nacos:
discovery:
server-addr: 172.16.109.118:8848
# sentinel服务
sentinel:
transport:
dashboard: 172.16.109.118:8080
2.3 编写微服务业务逻辑
/**
* 商品服务-模拟返回商品列表
*/
@RestController
@RequestMapping("api/goods")
public class GoodsService {
@Value("${spring.application.name}")
private String server;
@Value("${server.port}")
private String port;
public static final Logger logger = LoggerFactory.getLogger(GoodsService.class);
// 返回商品列表
@SentinelResource(value = "goods")
@GetMapping
public List<Goods> getAllGoods(HttpServletRequest httpRequest) throws InterruptedException {
// 设置等待时间
TimeUnit.MILLISECONDS.sleep(3000);
List<Goods> goods = new ArrayList<>();
goods.add(new Goods("电脑", 10));
goods.add(new Goods("手机", 20));
goods.add(new Goods("书籍", 30));
logger.info(port+":"+server+"服务被调用,调用者为:"+httpRequest.getRemoteAddr()+":"+httpRequest.getRemotePort());
return goods;
}
}
/**
* 商品类
*/
public class Goods implements Serializable {
// 商品名称
private String name;
// 商品价格
private Integer number;
public Goods(String name, Integer number) {
this.name = name;
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
}
2.4 启动微服务,通过Sentinel控制台进行监控
三、微服务的流量控制
3.1 通过JMeter模拟高并发流量
- 设置20000个并发请求
- 设置HTTP请求地址:
- 设置报告输出
- 启动JMeter,观察Sentinel控制台,可以看到QPS的实时状况。
3.2 设置流控规则进行流量控制
- 对Sentinel控制台中服务的资源增加流量控制规则
- 给微服务接口的QPS设置阈值
- 再次启动JMeter进行高并发测试,在控制台中进行观察,可以看到服务接口的QPS被限制在阈值以下。
- JMeter结果列表中可以观察到异常信息。
3.2 设置降级规则进行流量控制
- 对Sentinel控制台中服务的资源增加熔断降级规则
慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 在实时监控界面可以看到请求被拒绝,证明熔断降级规则生效
- 在JMeter结果表格中也出现了大量失败的调用请求
四、在微服务调用方进行流量控制
4.1 创建微服务调用者
4.1.1 加入OpenFeign组件
<-- pom.xml -->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
4.1.2 项目配置
server:
port: 8090
spring:
application:
name: user-consumer
cloud:
nacos:
discovery:
server-addr: 172.16.109.118:8848
# sentinel服务
sentinel:
transport:
dashboard: 172.16.109.118:8080
# 调用微服务超时时间设置
ribbon:
ConnectTimeout: 5000
ReadTimeout: 5000
# feign日志以什么级别监控哪个接口
logging:
level:
nacos.democonsumer.GoodService : debug
# 启用sentinel
feign:
sentinel:
enabled: true
4.1.3 主启动类增加注解
/**
* 主启动类
*/
// 启用Feign组件
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class DemoConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(DemoConsumerApplication.class, args);
}
}
4.1.4 增加微服务接口
- 通过@FeignClient调用微服务,并定义服务熔断时如何处理
/**
* 商品微服务接口
*/
@Component
// 增加服务容错处理,指定服务熔断时处理的类名
@FeignClient(value="goods-service",fallback =FallBackService.class )
public interface GoodService {
@GetMapping(value="/api/goods")
List<GoodsDTO> getGoods();
}
/**
* 服务熔断处理:返回空值
*/
@Component
public class FallBackService implements GoodService {
private final static Logger logger= LoggerFactory.getLogger(FallBackService.class) ;
@Override
public List<GoodsDTO> getGoods() {
logger.info("服务已熔断...");
return new ArrayList<>();
}
}
4.1.5 服务调用者调用程序
/**
* 用户消费者--调用nacos服务中心的商品服务,并对外提供RestFul接口测试
*/
@RestController
@RequestMapping("user/")
public class UserConsumer {
// 注入商品微服务接品
@Autowired
private GoodService goodService;
@GetMapping("/goods")
public User getUserGoods() {
User user = new User();
// 通过GoodsService接口调用商品微服务
try {
List<GoodsDTO> goods = goodService.getGoods();
user.setName("jack");
user.setGoods(goods);
} catch (Exception e){
throw new RuntimeException(e.getMessage());
}
return user;
}
}
4.2 启动微服务调用者,通过Sentinel控制台进行监控
- 通过HttpClient工具进行测试,调用正常
- 通过Sentinel控制台进行监控
4.3 对服务调用者中的服务提供方进行流量控制
- 对Sentinel控制台中服务的资源增加流量控制规则,在FeignClient中,Sentinel为Feign调用生成了资源名策略定义,定义规则为 [httpmethod :protocol://requesturl](比如GET:http://goods-service/api/goods)
- 为便于测试,故意将QPS的单机阈值设为0
- 使用HttpClient工具再次测试,由于我们在流量控制规则中已对QPS做了限制,服务调用请求已无法通过,故触发fallback,返回空值。
- 服务调用者日志信息提示服务已熔断…
小结
- Sentinel与Hystrix相比,更加轻量级:Sentinel对主流框架提供适配的 API,来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。
- Sentinel 提供了更加多样化的流量控制,熔断降级和系统负载保护手段。
- Sentine具备完善的实时监控和控制台。