在现代分布式系统中,如何有效地保护系统免受突发流量和故障的影响,是每个开发人员和架构师都需要思考的重要问题。在这样的背景下,Sentinel作为一个强大的系统保护和控制组件,为我们提供了降级、限流、熔断等多种策略,帮助我们更好地保障系统的稳定性和可用性。
https://sentinelguard.io/zh-cn/docs/quick-start.html
服务提供方
不能提供服务
时,怎么办?
- 存在整条链路服务(Service A、Service B、Service C)
- Service A 流量突然性增加,导致Service B 和Service C 流量也增加。
- Service C 因为抗不住请求,变得不可用。导致Service B的请求变得阻塞。
- 当Service B的资源耗尽,Service B就会变得不可用。
- 最后 Service A 不可用。
最开始处于
closed
状态,一旦检测到错误到达一定阈值,便转为open
状态;这时候会有个 reset timeout,到了这个时间了,会转移到
half open
状态;尝试放行一部分请求到后端,一旦检测成功便回归到
closed
状态,即恢复服务;
服务熔断和服务降级的区别?
服务降级大多是属于一种业务级别的处理,熔断属于框架层级的实现
开关降级
在配置中心配置一个开关(变量),在配置中心更改开关,决定哪些服务进行降级
提供者搭建集群(8170/8270),调用者调用,此时关闭提供者的一个服务(8270)
存在现象:访问8170成功访问,不能访问8270
略有卡顿,稍等片刻后,不再卡顿。
提供者搭建集群(8170/8270),调用者调用,此时关闭提供者的所有服务
现象:无法访问
添加坐标
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
修改yml文件,开启feign对sentinel的支持
feign:
sentinel:
enabled: true
修改启动类,开启feign
package com.czxy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author 薛慕昭
* @email 18716011269@163.com
*/
@SpringBootApplication
@EnableDiscoveryClient //服务发现
@EnableFeignClients //远程调用
public class TestNacosConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(TestNacosConsumerApplication.class, args );
}
}
修改Feign实现
package com.czxy.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author 薛慕昭
* @email 18716011269@163.com
*/
// @FeignClient(value = "服务名", path = "controller配置的路径" )
@FeignClient(value = "service-provider", fallback = EchoFeignFallback.class )
public interface EchoFeign {
// 与 nacos-provider-2.1>EchoController声明的方法的完全一致
@GetMapping("/echo/{string}")
public String echo(@PathVariable String string);
}
package com.czxy.feign;
import org.springframework.stereotype.Component;
/**
* @author 薛慕昭
* @email 18716011269@163.com
*/
@Component
public class EchoFeignFallback implements EchoFeign {
@Override
public String echo(String string) {
return "降级处理:" + string;
}
}
关闭服务提供者,测试
Sentinel Dashboard 是一个可视化
流控管理工具。
Sentinel Dashboard 是一个独立的项目,sentinel-dashboard-1.8.4.jar,需要使用 java -jar 运行
java -jar -Dserver.port=18080 sentinel-dashboard-1.8.4.jar
下载地址
https://github.com/alibaba/Sentinel/releases
添加坐标(已有)
<!-- 降级 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
配置yml
#server.port=8071
#spring.application.name=service-consumer
#spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#spring.cloud.sentinel.transport.dashboard=192.168.152.153:8080
#端口号
server:
port: 8071
spring:
application:
name: service-consumer #服务名
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos服务地址
sentinel:
transport:
dashboard: 127.0.0.1:18080
feign:
sentinel:
enabled: true
测试
先访问资源 http://localhost:8071/feign/echo/123
dashboard 登录
查看控制面板 http://localhost:18080/
通过 @SentinelResource 注解,设置监控点(定义控制资源、配置控制策略)
package com.czxy.nacos.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.czxy.nacos.feign.TestFeign;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author 薛慕昭
* @email 18716011269@163.com
*/
@RestController
@RequestMapping("/feign")
public class TestFeignController {
@Resource
private TestFeign testMyFeign;
@RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
@SentinelResource("/feign/echo")
public String echo(@PathVariable String str) {
return testMyFeign.echo(str);
}
}
测试
package com.czxy.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 薛慕昭
* @email 18716011269@163.com
*/
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/login")
public String login(String str) {
return "登录成功" + str;
}
@GetMapping("/register")
public String register(String str) {
return "注册成功";
}
}
@SentinelResource
注解的blockHandler
属性制定限流的处理函数package com.czxy.nacos.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 薛慕昭
* @email 18716011269@163.com
*/
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/login")
// 限流设置
@SentinelResource(value="login", blockHandler = "loginBlockHandler")
public String login(String str) {
return "登录成功" + str;
}
public String loginBlockHandler(String str , BlockException e) {
return str + ": 请稍后重试";
}
@GetMapping("/register")
public String register(String str) {
return "注册成功";
}
}
运行 sentinel-dashboard-1.8.4.jar
访问测试功能:http://localhost:8071/user/login?str=1234
通过 dashboard 设置限流
连续快速2次访问测试功能
package com.czxy.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.apache.commons.lang.math.RandomUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 薛慕昭
* @email 18716011269@163.com
*/
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/login")
// 限流设置
@SentinelResource(value="login", blockHandler = "loginBlockHandler")
public String login(String str) {
return "登录成功" + str;
}
public String loginBlockHandler(String str , BlockException e) {
return str + ": 请稍后重试";
}
@GetMapping("/register")
// 熔断降级
@SentinelResource(value="register", fallback = "registerFallback")
public String register(String str) {
int r = RandomUtils.nextInt(10);
if(r < 5) {
int i = 1 / 0;
}
return "注册成功";
}
public String registerFallback(String str) {
return str + ": 熔断降级";
}
}
成功
熔断降级
k(String str) {
return str + “: 熔断降级”;
}
}
### 2 测试
* 成功
[外链图片转存中...(img-EtoFL8NX-1705283262451)]
* 熔断降级
[外链图片转存中...(img-SNdK9B8Y-1705283262451)]
### 3 降级操作
* 慢调用比例:
* RT:平均响应时间
* 比例阈值:
* 熔断时长:
* 最小请求数:
* 异常比例:每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态
* 异常数:当资源近 1 分钟的异常数目超过阈值之后会进行熔断
[外链图片转存中...(img-7eNiRPkH-1705283262452)]
## 六. 限流和降级的区别?
* 限流是通过设置QPS(每秒查询率)/线程数,将超过阈值部分拒绝处理;
* 服务降级是监控请求响应时间、响应异常比例、异常数量;超过限定阈值,将进行服务降级熔断,一定时间内不可用;
# 结尾
Sentinel的降级、限流、熔断等功能为我们构建健壮的分布式系统提供了强有力的支持。通过合理地设置各项保护和控制策略,我们可以更好地抵御恶劣环境下的挑战,保持系统的稳定和可靠。因此,在设计和实现分布式系统时,充分利用Sentinel的功能将是一个明智的选择,它将为系统的高可用性和稳定性保驾护航。