服务雪崩效应:因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程,就叫服务雪崩效应
在不做任何处理的情况下,服务提供者不可用会导致消费者清求线程强制等待,而造成系统资源耗尽。加入超时机制,一旦超时,就释放资源。由于释放资源速度较快,一定程度上可以抑制资源耗尽的问题。
服务限流
隔离
原理:用户的求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,则会进行降级处理,用户的请求不会被堵塞,至少可以看到一个执行结果((例如返回友好的提示信息),而不是无休止的等待或者看到系统崩溃。
远程服务不稳定或网络抖动时暂时关闭,就叫服务熔断。
有服务熔断,必然要有服务降级。
所谓降级,就是当某个服务熔断之后,服务将不再被调用,此时客户端可以自己准备一个本地的fallback(回退)回调,返回一个缺省值,例如:(备用接口缓存/mock数据),这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。
sentinel是阿里巴巴开源的,面向分布式服务架构的高可用防护组件
sentinel具有以下特征:
sentinel和Hystrix对比
Sentinel 可以简单的分为Sentinel核心库和Dashboard
核心库不依赖Dashboard,但是结合Dashboard可以取得最好的效果。
1.创建项目导入依赖
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.7.RELEASEversion>
<relativePath/>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>sentinel-demoartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-coreartifactId>
<version>1.8.0version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.20version>
dependency>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-annotation-aspectjartifactId>
<version>1.8.0version>
dependency>
dependencies>
project>
2.controller层
package com.tian.sentinel.controller;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
@RestController
@Slf4j
public class HelloController {
private static final String RESOURCE_NAME ="hello";
private static final String USER_RESOURCE_NAME="user";
private static final String DEGRADE_RESOURCE_NAME="degrade";
//进行流控
@RequestMapping(value = "/hello")
public String hello(){
Entry entry=null;
try {
//sentinel针对资源进行限制
entry= SphU.entry(RESOURCE_NAME);
//被保护的业务逻辑
String str = "hello world";
log.info("-------"+str+"--------");
return str;
}catch (BlockException e1){
//资源访问阻止,被限流或被降级
//进行对应的处理操作
log.info("block!");
return "被流控了!";
}catch (Exception ex){
//若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(ex,entry);
}finally {
if (entry != null) {
entry.exit();
}
}
return null;
}
@PostConstruct
private static void initFlowRules(){
//流控规则
ArrayList<FlowRule> rules = new ArrayList<>();
//流控
FlowRule rule = new FlowRule();
//为那个资源进行流控
rule.setResource(RESOURCE_NAME);
//设置受保护的资源阈值
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(1);
rules.add(rule);
//加载配置好的规则
FlowRuleManager.loadRules(rules);
}
}
3.主启动类
4.application.yml
server:
port: 8060
5.测试访问
http://localhost:8060/hello
一秒点击一次 访问没问题 显示 hello world
一秒点击2次或以上,显示被限流了
改善接口中资源定义和被流控降级后的处理方法
1.添加依赖
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-annotation-aspectjartifactId>
<version>1.8.0version>
dependency>
2.配置@bean
//注解支持的配置Bean
@Bean
public SentinelResourceAspect sentinelResourceAspect(){
return new SentinelResourceAspect();
}
3.pojo
@Data
public class User {
private final String username;
}
4.controller
package com.tian.sentinel.controller;
@RestController
@Slf4j
public class HelloController {
private static final String RESOURCE_NAME ="hello";
private static final String USER_RESOURCE_NAME="user";
private static final String DEGRADE_RESOURCE_NAME="degrade";
@PostConstruct
private static void initFlowRules(){
//流控规则
ArrayList<FlowRule> rules = new ArrayList<>();
//流控
FlowRule rule = new FlowRule();
//为那个资源进行流控
rule.setResource(RESOURCE_NAME);
//设置受保护的资源阈值
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(1);
rules.add(rule);
FlowRule rule2 = new FlowRule();
rule2.setResource(USER_RESOURCE_NAME);
rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule2.setCount(1);
rules.add(rule2);
//加载配置好的规则
FlowRuleManager.loadRules(rules);
}
/**
* value 定义资源
* blockHandler 设置 流控降级后的处理方法 (默认该方法必须声明在同一个类)
* 如果不想在同一个类,用blockHandlerClass 但是方法必须加上static关键字
* fallback 当接口出现了异常,就可以交给fallback指定的方法进行处理
* blockHandler > fallback
* fallbackClass 和 用blockHandlerClass一样的用法
* @return
*/
@RequestMapping("/user")
@SentinelResource(value = USER_RESOURCE_NAME,
fallback = "fallbackHandlerForGetUser",
blockHandler = "blockHandlerForGetUser")
public User getUser(String id){
// int i=1/0;
return new User("tian");
}
/**
* 1.一定要public
* 2.返回值一定要和源方法(上面的)保证一致,包含源方法的参数.顺序也要一样
* 3.必须在参数最后添加BlockException,可以区分是什么规则的处理方法
* @return
*/
public User blockHandlerForGetUser(String id,BlockException ex){
ex.printStackTrace();
return new User("流控!!!");
}
public User fallbackHandlerForGetUser(String id,Throwable e){
e.printStackTrace();
return new User("异常处理");
}
}
5.配置文件
server:
port: 8060
6.主启动类
@SpringBootApplication
public class StartApplication {
public static void main(String[] args) {
SpringApplication.run(StartApplication.class,args);
}
//注解支持的配置Bean
@Bean
public SentinelResourceAspect sentinelResourceAspect(){
return new SentinelResourceAspect();
}
}
7.测试访问
http://localhost:8060/user
现象:点击速度快,会被流控
同上controller再加上代码
@PostConstruct //初始化
public void initDegradeRule(){
//降级规则 异常
ArrayList<DegradeRule> degradeRules = new ArrayList<>();
DegradeRule degradeRule = new DegradeRule();
degradeRule.setResource(DEGRADE_RESOURCE_NAME);
//设置规则测率 : 异常数
degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
//出发熔断异常数: 2
degradeRule.setCount(2);
//出发熔断的最小请求数 : 2
degradeRule.setMinRequestAmount(2);
//统计时长 单位 ms 1分钟
degradeRule.setStatIntervalMs(60*1000); //时间太短不好测
//一分钟内 :执行了2次,出现了2次异常
//熔断持续时长: 10s
//一但出发熔断,再次请求对应的接口就会直接调用降级方法
//10s过后----半开状态,恢复接口请求调用,再次熔断,不会根据设置的条件进行
degradeRule.setTimeWindow(10);
degradeRules.add(degradeRule);
DegradeRuleManager.loadRules(degradeRules);
}
@RequestMapping("/degrade")
@SentinelResource(value = DEGRADE_RESOURCE_NAME,
entryType = EntryType.IN,
blockHandler = "blockHandlerForFb")
public User degarde(String id) throws InterruptedException {
// 异常数\比例
throw new RuntimeException("异常---");
}
public User blockHandlerForFb(String id,BlockException ex){
ex.printStackTrace();
return new User("流控!!!");
}
访问 http://localhost:8060/degrade 页面都是报500错误访问几次页面显示 流控 (在10s内一直访问页面显示都是流控,10s后的话页面显示500)
下载控制台jar包并在本地启动 下载地址
java -jar sentinel-dashboard-1.8.0.jar 默认端口8080
访问localhost:8080
账号sentinel
密码sentinel
自定义端口 用户名 密码 启动
java -Dserver.port=8888 -Dsentinel.dashboard.auth.username=tian -Dsentinel.dashboard.auth.password=123 -jar sentinel-dashboard-1.8.0.jar
客户端需要引入Transport模块来与sentinel控制台进行通信
导入依赖
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-transport-simple-httpartifactId>
<version>1.8.0version>
dependency>
idea中配置
-Dcsp.sentinel.dashboard.server=127.0.0.1:8888
启动项目 随便访问一个 localhost:8060/user
发现sentinel监控有东西了
1.引入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
2.添加yml配置文件
server:
port: 8861
spring:
application:
name: order-sentinel
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8888
3.controller
@RestController
@RequestMapping("/order")
public class OrderController {
@RequestMapping("/add")
public String add(){
System.out.println("下单成功!");
return "hello world";
}
}
4.主启动类
5.访问测试 http://localhost:8861/order/add
6.发现sentinel控制台 有记录