一、Sentinel简介
是什么:
Spring Cloud Alibaba Sentinel 是面向于云原生微服务的高可用流控防护组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助用户保障微服务的稳定性。 一句话解释,就是之前学习过的 Hystrix 升级版。
二、安装并运行Sentinel
去哪下载?
先访问 Sentinel 的 GitHub 地址:https://github.com/alibaba/Sentinel,找到并单击 releases 菜单:https://github.com/alibaba/Sentinel/releases
Sentinel组件由两部分组成:
-
核心库(Java客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
-
控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
运行Sentinel:
注意版本号,可能不同的版本号,端口号可能不同
运行前提:需要最低Java8 JDK环境OK,8080端口不能被占用。在原来8080端口会被说为是 Tomcat 默认端口,不知道为什么,Sentinel 确选择了8080作为自己的端口号。
在自己下载的 Sentinel Jar包目录中,长按 Shift 在此处打开 Powershell 窗口,输入 start cmd 命令并打开 Windows 终端窗口,
接着输入 java -jar sentinel-dashboard-1.7.1.jar 命令成功启动 Sentinel 控制台如下:
如何验证是否运行成功呢?
很简单,命令运行成功后直接访问http://localhost:8080/,会转发到 Sentinel 登录页面,输入默认登录账号密码均为 sentinel,都是小写
1. 整合步骤
前提已配置号Nacos注册中心
Nacos+sentinel 需要的maven依赖
<!-- SpringCloud ailibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- SpringCloud ailibaba sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
1.1添加Maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>0.2.1.RELEASE</version>
</dependency>
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinal-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址
server-addr: localhost:8848
sentinel:
transport:
#配置Sentin dashboard地址
dashboard: localhost:8080
# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口
port: 8719
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
feign:
sentinel:
enabled: true #激活Sentinel 对Feign的支持
contorller
@RestController
@Slf4j
public class FlowLimitController {
@GetMapping("/testA")
public String testA() {
return "----testA";
}
@GetMapping("/testB")
public String testB() {
return "----testB";
}
@GetMapping("/testD")
public String testD() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("testD 测试RT");
return "----testD";
}
@GetMapping("/testE")
public String testE() {
log.info("testE 测试异常数");
int age = 10 / 0;
return "----testE 测试异常数";
}
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false)String p1,
@RequestParam(value = "p2",required = false)String p2) {
return "----testHotKey";
}
public String deal_testHotKey(String p1, String p2, BlockException exception) {
return "----deal_testHotKey, o(╥﹏╥)o"; // sentinel的默认提示都是: Blocked by Sentinel (flow limiting)
}
}
启动项目
,但是此刻Sentinel中什么都没有,因为Alibaba Sentinel 是懒加载,需要发送一次请求访问即可,随便访问一个controller
流控规则:
1.默认QPS
- 这里做一个最简单的配置:
- 阈值类型选择:QPS
- 单机阈值:1
综合起来的配置效果就是,该接口的限流策略是每秒最多允许1个请求进入。
阀值类型的QPS和线程数的区别:
QPS (每秒钟的请求数量) :当调用该api的QPS达到阈值的时候,进行限流
线程数:当调用该api的线程数达到阈值的时候,进行限流
2 关联: 当关联的资源达到阈值时,就限流自己
b惹事导致A挂了
模拟访问B
结论:大批量线程高并发访问B,导致A失效了
2 预热:
应用场景:
3:排队等待
降级规则:
效果演示:
2:异常比例
3:异常数:
热点key限流
官网:https://github.com/alibaba/Sentinel/wiki/热点参数限流
效果演示:
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")//value随意,但是必须唯一,为了规范和请求保持一致
public String testHotKey(@RequestParam(value = "p1",required = false)String p1,
@RequestParam(value = "p2",required = false)String p2) {
return "----testHotKey";
}
public String deal_testHotKey(String p1, String p2, BlockException exception) {
return "----deal_testHotKey, o(╥﹏╥)o"; // sentinel的默认提示都是: Blocked by Sentinel (flow limiting)
}
开始配置
错误页面:(有blockHandler的兜底方法)
错误页面:(无blockHandler的兜底方法)
参数例外项
手动异常:
结论:
系统规则:(系统入口的限流,而非细粒度的rest)
对整个项目进项统一限流,相当于系统的大门
官网:
@SentinelResource
@RestController
public class RateLimitController {
@GetMapping("/byResource")
@SentinelResource(value = "byResource", blockHandler = "handleException")
public CommonResult byResource() {
return new CommonResult(200, "按资源名称限流测试ok", new Payment(2020L, "serial001"));
}
public CommonResult handleException(BlockException exception) {
return new CommonResult(444, exception.getClass().getCanonicalName() + "\t 服务不可用");
}
}
无兜底方法;
客户自定义限流处理逻辑:
/**
* @author
* 创建customerBlockHandler类用于自定义限流处理逻辑
* @date 2020-09-23 17:20
*/
public class CustomerBlockHandler {
public static CommonResult handlerException(BlockException exception) {
return new CommonResult(4444, "按客户自定义,global handlerException----------1");
}
public static CommonResult handlerException2(BlockException exception) {
return new CommonResult(4444, "按客户自定义2,global handlerException----------2");
}
}
@RestController
public class RateLimitController {
//使用自定义的限流处理器
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException")
public CommonResult byUrl() {
return new CommonResult(200, "按url限流测试OK", new Payment(2020L, "serial001"));
}
//CustomerBlockHandler
@GetMapping("/rateLimit/CustomerBlockHandler")
@SentinelResource(value = "customerBlockHandler", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
public CommonResult CustomerBlockHandler() {
return new CommonResult(200, "按客户自定义限流测试OK", new Payment(2020L, "serial003"));
}
}
服务熔断功能(重要)
1:ribbon系列:服务调用,熔断
9003/9004pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springCloud</artifactId>
<groupId>com.george.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-provider-payment9003</artifactId>
<dependencies>
<!-- SpringCloud alibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- web组件 -->
<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>
</dependencies>
</project>
9003yml
server:
port: 9003
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos 地址
management:
endpoints:
web:
exposure:
include: "*"
9003Controller
@RestController
public class PaymentController {
public static HashMap<Long, Payment> hashMap = new HashMap<>();
/**
* 模拟数据库,为省事,不写数据库层面
*/
static {
hashMap.put(1L, new Payment(1L, "28a8c1e3bc2742d8848569891fb42181"));
hashMap.put(2L, new Payment(2L, "28a4c1e3bc2742d8848569891fb42181"));
hashMap.put(3L, new Payment(3L, "28a5c1e3bc2742d8848569891fb42181"));
}
@Value("${server.port}")
private String serverPort;
//需要被84服务器调用
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
Payment payment = hashMap.get(id);
CommonResult<Payment> result = new CommonResult<>(200, "from mysql,serverPort: " + serverPort, payment);
return result;
}
}
84pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springCloud</artifactId>
<groupId>com.george.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-consumer-nacos-order84</artifactId>
<dependencies>
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- SpringCloud alibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- web组件 -->
<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>
</project>
84yml
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
Sentinel实现熔断无配置
/**
* @author Yang Hao
* @date 2020-09-24 09:34
*/
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
84conrotller
/**
* @author Yang Hao
* @date 2020-09-24 09:35
*/
@RestController
public class CircleBreakerController {
//需要调用9003的微服务
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@Resource
private PaymentService paymentService;
@RequestMapping("/consumer/fallback/{id}")
// @SentinelResource(value = "fallback") //没有配置,只是让sentinel检测到
// @SentinelResource(value = "fallback", fallback = "handlerFallback") //fallback 只负责业务异常,java运行异常
// @SentinelResource(value = "fallback", blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
// @SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler") //fallback和blockHandler 都配置,如果出现异常错误会,进入blockHandler 处理
@SentinelResource(value = "fallback",
fallback = "handlerFallback",
blockHandler = "blockHandler",
exceptionsToIgnore = {IllegalArgumentException.class}
)// exceptionsToIgnore = {IllegalArgumentException.class}出现该异常就不会走兜底方法 比如id等于4会出现此异常,会直接抛出java异常
public CommonResult<Payment> fallback(@PathVariable("id") Long id) {
//此方法是调用服务9003的controller方法
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
/**
* 本例子是 fallback handlerFallback
*
* @param id
* @param e
* @return
*/
public CommonResult handlerFallback(@PathVariable("id") Long id, Throwable e) {
Payment payment = new Payment(id, "null");
return new CommonResult(444, "兜底异常handlerFallback,exception内容" + e.getMessage(), payment);
}
//--------------------------------- OpenFeign ---------------------------------
/**
* 本例子是 blockHandler
*
* @param id
* @param blockException
* @return
*/
public CommonResult blockHandler(@PathVariable("id") Long id, BlockException blockException) {
Payment payment = new Payment(id, "null");
return new CommonResult(445, "blockHandler-sentinel限流,无此流水:blockException" + blockException.getClass().getCanonicalName(), payment);
}
@GetMapping(value = "/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
return paymentService.PaymentSQL(id);
}
}
2:feign系类:服务调用,熔断系列
pom.xml
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
84.yml
```yaml
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
# 对 Feign 的支持
feign:
sentinel:
enabled: true
主启动类记得启动激活
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class OrderNacosMain84 {
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain84.class, args);
}
}
server
/**
* @author
* @date 2020-09-24 09:35
*/
@FeignClient(value = "nacos-payment-provider", fallback = PaymentFallbackServiceImpl.class)
public interface PaymentService {
@GetMapping(value = "/paymentSQL/{id}")
CommonResult<Payment> PaymentSQL(@PathVariable("id") Long id);
}
server实现类降级处理
/**
* @author
* @date 2020-09-24 09:35
*/
@Service
public class PaymentFallbackServiceImpl implements PaymentService {
@Override
public CommonResult<Payment> PaymentSQL(Long id) {
return new CommonResult<>(444444444, "服务降级返回,----PaymentFallbackServiceImpl", new Payment(id, "errorSerial"));
}
}
规则持久化;
84pom.xml
<!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
84yml
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinal-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址
server-addr: localhost:8848
sentinel:
transport:
#配置Sentin dashboard地址
dashboard: localhost:8080
# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口
port: 8719
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service #微服务名称
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
feign:
sentinel:
enabled: true #激活Sentinel 对Feign的支持
配置nacos
快速访问测试接口:
停止8401再看sentinel:
重启8401: