服务雪崩预防Sentinel

简介: 服务雪崩预防Sentinel

服务雪崩效应

在分布式系统中,由于网络原因或自身的原因,服务一般无法保证 100% 可用。如果一个服务出现了

问题,调用这个服务就会出现线程阻塞的情况,此时若有大量的请求涌入,就会出现多条线程阻塞等

待,进而导致服务瘫痪。

由于服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是

服务故障的 “雪崩效应” 。

服务容错核心思想

  • 不被上游请求压垮
  • 不被下游响应拖垮
  • 不被外界环境影响(运维配置系统规则)

常见容错方案Sentinel

在pom.xml中加入下面依赖

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

安装Sentinel控制台

Sentinel 提供一个轻量级的控制台, 它提供机器发现、单机资源实时监控以及规则管理等功能。

  1. 下载jar包,解压到文件夹
    https://github.com/alibaba/Sentinel/releases
  2. 启动控制台
# 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目)
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.0.jar
  1. 修改服务配置,加入控制台的配置
spring:
  cloud:
    sentinel:
      transport:
        port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可
        dashboard: localhost:8080 # 指定控制台服务的地址
  1. 通过浏览器访问localhost:8080 进入控制台 ( 默认用户名密码是 sentinel/sentinel )
    注意:sentinel控制台界面,是懒加载,必须先访问服务后,在sentinel控制台界面才会显示

流控规则

表示:每秒请求量大于3的时候开始限流

sentinel共有三种流控模式,分别是:

直接(默认):接口达到限流条件时,开启限流

关联:当关联的资源达到限流条件时,开启限流 [适合做应用让步]

链路:当从某个接口过来的资源达到限流条件时,开启限流

降级规则

表示:响应时间超过1ms时,接下来5s内服务降级,5s后服务恢复正常,进行下一轮判断。

降级规则就是设置当满足什么条件的时候,对服务进行降级。Sentinel提供了三个衡量条件:

  1. 平均响应时间 :当资源的平均响应时间超过阈值(以 ms 为单位)之后,资源进入准降级状态。
    如果接下来 1s 内持续进入 5 个请求,它们的 RT都持续超过这个阈值,那么在接下的时间窗口
    (以 s 为单位)之内,就会对这个方法进行服务降级。
  2. 异常比例:当资源的每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态,即在接下的
    时间窗口(以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0,
    1.0]。

  3. 异常数 :当资源近 1 分钟的异常数目超过阈值之后会进行服务降级。注意由于统计时间窗口是分
    钟级别的,若时间窗口小于 60s,则结束熔断状态后仍可能再进入熔断状态。

热点规则

热点参数流控规则是一种更细粒度的流控规则, 它允许将规则具体到参数上。

热点规则简单使用

第1步: 编写代码

@RequestMapping("/order/message3")
    @SentinelResource("message3") //注意这里必须使用这个注解标识,否则热点规则不生效
    public String message3(String name, Integer age) {
        return "message3" + name + age;
    }

第2步: 配置热点规则

表示:1s内超过1个请求,接下来10s内服务限流,10s后服务恢复正常,进行下一轮判断。

第3步: 分别用两个参数访问,会发现只对第一个参数限流了

热点规则增强使用

参数例外项允许对一个参数的具体值进行流控

表示:第一个参数值为zhu的时候,限流阈值为20,即每秒超过20个请求才会限流。

授权规则

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源

访问控制的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过:

  • 若配置白名单,则只有请求来源位于白名单内时才可通过;
  • 若配置黑名单,则请求来源位于黑名单时不通过,其余的请求通过。

比如:A调用服务C都放行,B调用服务C都限流。

上面的资源名和授权类型不难理解,但是流控应用怎么填写呢?

其实这个位置要填写的是来源标识,Sentinel提供了 RequestOriginParser 接口来处理来源。

只要Sentinel保护的接口资源被访问,Sentinel就会调用 RequestOriginParser 的实现类去解析

访问来源。

第1步: 自定义来源处理规则

@Component
public class RequestOriginParserDefinition implements RequestOriginParser {
    //定义区分来源: 本质作用是通过request域获取到来源标识
    //app  pc
    //然后 交给流控应用 位置进行匹配
    @Override
    public String parseOrigin(HttpServletRequest request) {
        String serviceName = request.getParameter("serviceName");
        if (StringUtils.isEmpty(serviceName)){
            throw new RuntimeException("serviceName is not empty");
        }
        return serviceName;
    }
}

第2步: 授权规则配置

这个配置的意思是只有serviceName=pc不能访问(黑名单)

第3步: 访问 http://localhost:8091/order/message1?serviceName=pc观察结果

自定义规则异常返回

//自定义异常返回页面(区分各种限流和降级等异常)
@Component
public class ExceptionHandlerPage implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
        response.setContentType("application/json;charset=utf-8");//处理中文乱码
        ResponseData responseData = null;
        //BlockException  异常接口,包含Sentinel的五个异常
        //  FlowException  限流异常
        //  DegradeException  降级异常
        //  ParamFlowException  参数限流异常
        //  AuthorityException  授权异常
        //  SystemBlockException  系统负载异常
        if (e instanceof FlowException) {
            responseData = new ResponseData(-1, "接口被限流了...");
        } else if (e instanceof DegradeException) {
            responseData = new ResponseData(-2, "接口被降级了...");
        }
        response.getWriter().write(JSON.toJSONString(responseData));
    }
}
@Data
@AllArgsConstructor //全参构造
@NoArgsConstructor //无参构造
class ResponseData {
    private int code;
    private String message;
}

@SentinelResource的使用

在定义了资源点之后,我们可以通过Dashboard来设置限流和降级策略来对资源点进行保护。同时还能

通过@SentinelResource来指定出现异常时的处理策略。

@Service
@Slf4j
public class OrderServiceImpl3 {
    int i = 0;
    //定义一个资源
    //定义当资源内部发生异常的时候的处理逻辑
    //blockHandler  定义当资源内部发生了BlockException应该进入的方法[捕获的是Sentinel定义的异常]
    //fallback      定义当资源内部发生了Throwable应该进入的方法
    @SentinelResource(
            value = "message",
            blockHandlerClass = OrderServiceImpl3BlockHandler.class,
            blockHandler = "blockHandler",
            fallbackClass = OrderServiceImpl3Fallback.class,
            fallback = "fallback"
    )
    public String message(String name) {
        i++;
        if (i % 3 == 0) {
            throw new RuntimeException();
        }
        return "message";
    }
}
//OrderServiceImpl3对应的BlockException处理的类
@Slf4j
public class OrderServiceImpl3BlockHandler {
    //blockHandler
    //要求:
    //1 当前方法的返回值和参数要跟原方法一致(且是静态方法)
    //2 但是允许在参数列表的最后加入一个参数BlockException, 用来接收原方法中发生的异常
    public static String blockHandler(String name, BlockException e) {
        //自定义异常处理逻辑
        log.error("触发了BlockException,内容为{}", e);
        return "BlockException";
    }
}
//OrderServiceImpl3对应的Throwable处理的类
@Slf4j
public class OrderServiceImpl3Fallback {
    //fallback
    //要求:
    //1 当前方法的返回值和参数要跟原方法一致
    //2 但是允许在参数列表的最后加入一个参数Throwable, 用来接收原方法中发生的异常
    public static String fallback(String name, Throwable e) {
        //自定义异常处理逻辑
        log.error("触发了Throwable,内容为{}", e);
        return "Throwable";
    }
}

Sentinel规则持久化

通过前面的讲解,我们已经知道,可以通过Dashboard来为每个Sentinel客户端设置各种各样的规

则,但是这里有一个问题,就是这些规则默认是存放在内存中,极不稳定,所以需要将其持久化。

  1. 编写处理类
import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Value;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
 * 流控规则持久化
 */
public class FilePersistence implements InitFunc {
    @Value("spring.application.name")
    private String appcationName;
    @Override
    public void init() throws Exception {
        String ruleDir = System.getProperty("user.home") + "/sentinel-rules/" + appcationName;
        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath = ruleDir + "/authority-rule.json";
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
        this.mkdirIfNotExits(ruleDir);
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(paramFlowRulePath);
        // 流控规则
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath,
                flowRuleListParser
        );
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
        // 降级规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
                degradeRulePath,
                degradeRuleListParser
        );
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
                degradeRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
        // 系统规则
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
                systemRulePath,
                systemRuleListParser
        );
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
                systemRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
        // 授权规则
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
                authorityRulePath,
                authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
                authorityRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
        // 热点参数规则
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
                paramFlowRulePath,
                paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
                paramFlowRulePath,
                this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }
    private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<FlowRule>>() {
            }
    );
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {
            }
    );
    private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<SystemRule>>() {
            }
    );
    private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<AuthorityRule>>() {
            }
    );
    private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<ParamFlowRule>>() {
            }
    );
    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }
    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }
    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }
}
  1. 添加配置
    在resources下创建配置目录 META-INF/services ,然后添加文件
    com.alibaba.csp.sentinel.init.InitFunc
    在文件中添加配置类的全路径
    com.itheima.config.FilePersistence

Feign整合Sentinel

第1步: 引入sentinel的依赖

<!--sentinel客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

第2步: 在配置文件中开启Feign对Sentinel的支持

# 开启feign对sentinel的支持
feign:
  sentinel:
    enabled: true

第3步: 创建容错类

//这是容错类,他要求我们要是实现一个FallbackFactory<要为哪个接口产生容错类>
@Slf4j
@Service
public class ProductServiceFallbackFactory implements FallbackFactory<ProductService> {
    //Throwable  这就是fegin在调用过程中产生异常
    @Override
    public ProductService create(Throwable throwable) {
        return new ProductService() {
            @Override
            public Product findByPid(Integer pid) {
                log.error("{}",throwable);//打印异常
                Product product = new Product();
                product.setPid(-100);
                product.setPname("商品微服务调用出现异常了,已经进入到了容错方法中");
                return product;
            }
            @Override
            public void reduceInventory(Integer pid, Integer number) {
            }
        };
    }
}

第4步: 为接口指定容错类

//value用于指定调用nacos下哪个微服务
//fallback 指定当调用出现问题之后,要进入到哪个类中的同名方法之下执行备用逻辑
//fallbackFactory 指定当调用出现问题之后,要进入到哪个类中的同名方法之下执行备用逻辑,并且可以在日志中打印异常信息
@FeignClient(
        value = "service-product",
//        fallback = ProductServiceFallback.class,
        fallbackFactory = ProductServiceFallbackFactory.class
)
public interface ProductService {
    //@FeignClient的value +  @RequestMapping的value值  其实就是完成的请求地址  "http://service-product/product/" + pid
    //指定请求的URI部分
    @RequestMapping("/product/{pid}")
    Product findByPid(@PathVariable Integer pid);
    //扣减库存
    //参数一: 商品标识
    //参数二:扣减数量
    @RequestMapping("/product/reduceInventory")
    void reduceInventory(@RequestParam("pid") Integer pid,
                         @RequestParam("number") Integer number);
}
目录
相关文章
|
6月前
|
运维 网络协议 安全
网络故障分析
了解一些运维工作所必须要掌握的网络命令(MTR、traceroute 等)的原理和使用,并进行演示
|
2天前
|
存储 运维 监控
|
5天前
|
运维 监控 NoSQL
|
4月前
|
Sentinel
一文速通Sentinel熔断及降级规则
一文速通Sentinel熔断及降级规则
|
7月前
|
运维 监控 测试技术
故障治理:如何进行故障复盘
故障复盘的重要性无需多说,每一次故障都是宝贵的学习机会,本人接手故障复盘工作已经半年有余,从一开始的手足无措,慢慢变得游刃有余。以下内容为本人从网上查阅学习多个专家经验,并结合工作经历总结而来,仅供参考。
|
监控 算法 Java
Sentinel之流控规则
Sentinel之流控规则
194 0
|
Java 应用服务中间件 Sentinel
【Sentinel】隔离和降级
【Sentinel】隔离和降级
263 0
【Sentinel】隔离和降级
|
Java 应用服务中间件 开发者
Sentinel 实现服务降级效果 | 学习笔记
快速学习 Sentinel 实现服务降级效果
99 0
|
缓存 运维 监控
IT硬件故障的主要原因和预防的最佳实践
企业组织面临的超过 45% 的网络中断完全是由于硬件故障造成的,因此 24x7 全天候监控硬件至关重要
243 0
IT硬件故障的主要原因和预防的最佳实践
|
域名解析 运维 Prometheus
k8s故障检测与自愈(一)
k8s故障检测与自愈(一)
k8s故障检测与自愈(一)