Sentinel规则持久化以及对RestTemplate和Feign的流控熔断

导读:本篇文章讲解 Sentinel规则持久化以及对RestTemplate和Feign的流控熔断,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

Sentinel持久化:

  Sentinel中文文档:
三种模式

(1)原始模式:

这是Sentinel默认模式,该模式规则下不持久化,重启微服务之后配置的限流降级等规则全部失效。

(2)Pull模式:(拉模式)

Pull模式
  如官方文档的上图所示,Sentinel Dashboard推送规给微服务,微服务将规则更新到内存,同时将规则更新到本地文件,以此来实现规则的持久化。
  配置依赖:

<!--sentinel-datasource-extension数据源扩展-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-extension</artifactId>
</dependency>
**
 * Pull模式规则持久化,持久化到本地生成文件
 */
public class FileDataSourceInit implements InitFunc {

    @Override
    public void init() throws Exception {
        //可以根据需要指定规则文件的位置
        String ruleDir = System.getProperty("user.home") + "/sentinel/rules";
		//流控
        String flowRulePath = ruleDir + "/flow-rule.json";
        //降级
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        //热点
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
        //系统
        String systemRulePath = ruleDir + "/system-rule.json";
        //授权
        String authorityRulePath = ruleDir + "/authority-rule.json";

        this.mkdirIfNotExits(ruleDir);

        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(paramFlowRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);

        // 流控规则:可读数据源
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath,
                flowRuleListParser
        );
        // 将可读数据源注册至FlowRuleManager
        // 这样当规则文件发生变化时,就会更新规则到内存
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        // 流控规则:可写数据源
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        // 将可写数据源注册至transport模块的WritableDataSourceRegistry中
        // 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
        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<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
                paramFlowRulePath,
                paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        // 热点参数规则:可写数据源
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
                paramFlowRulePath,
                this::encodeJson
        );
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);


        // 系统规则:可读数据源
        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);
    }


    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);
    }
}

  在META-INF/service/com.alibaba.csp.sentinel.init.InitFunc里面配置下写入文件规则的全类名:
配置持久化规则生成本地文件
  在Sentinel Dashboard控制台配置规则就写入如下文件
本地持久化规则
Pull模式实现方法好处是简单,不引入新的依赖,坏处是无法保证监控数据的一致性。

(3)Push模式(推模式):

Push模式
  远程配置中心个人使用的是Nacos:这种方式是将规则存储在nacos配置中心,微服务从nacos配置中心获取规则,这种方式有更好的实时性和一致性保证,生产环境建议使用该方式(支持Nacos、ZooKeeper、Apollo等)

Push模式(推模式)规则持久化到Nacos:

1、添加sentinel-datasource-nacos依赖;

<!--sentinel数据持久化-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

2、application.properties配置持久化数据源;

#application.properties配置持久化数据源;配合nacos一起使用
#注册到Nacos集群环境
spring.cloud.sentinel.datasource.ds1.nacos.serverAddr=47.110.237.194:80
spring.cloud.sentinel.datasource.ds1.nacos.dataId=${spring.application.name}.json
#默认分组
spring.cloud.sentinel.datasource.ds1.nacos.groupId=DEFAULT_GROUP
#JSON格式
spring.cloud.sentinel.datasource.ds1.nacos.dataType=json
#流控规则
spring.cloud.sentinel.datasource.ds1.nacos.ruleType=flow

Nacos持久化详情
Sentinel控制台同步Nacos信息
  改变Nacos里面的Sentinel规则可以同步到Sentinel Dashboard控制台,但是修改Sentinel控制台的规则不能同步到Nacos的持久化规则里面

Spring Cloud Alibaba Sentinel Dashboard通信原理:

原理图
微服务暴露给Sentinel Dashboard的API接口列表:http://localhost:8719/api
Sentinel Dashboard控制台配置修改:

#懒加载、饥饿加载:true表示饥饿加载
#开启饥饿加载  就是一开始启动的时候一次性创建完,而不是设置为false的时候懒加载谁调用用到的时候才加载
spring.cloud.sentinel.eager=true

Spring Cloud Alibaba Sentinel三种保护应用方式:

1、直接用配置拦截所有controller的请求路径(对controller请求埋点):

  Sentinel为Spring Boot程序提供了一个starter依赖,由于Sentinel starter默认情况下会给所有的HTTP服务提供限流埋点,所以Spring Boot项目中所有的controller都可以收到sentinel的保护,项目当中引入做下依赖:

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

还需要为sentinel配置保护规则,底层是用过一个拦截器
com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor
对请求拦截实现。
配置文件:

#关闭sentinel对controller的url的保护(默认为true)
spring.cloud.sentinel.filter.enabled=false

2、通过自定义代码实现埋点:

 /**
     *
     * @return
     */
    @GetMapping("/test/{app}")
    public String test3(@PathVariable(name = "app") String app){
        System.out.println("/test3/{app} --> " + app);

        ContextUtil.enter("test3");
        Entry entry = null;
        try {
            entry = SphU.entry("test3");
            //受sentinel保护的代码 start
            //int a = 10 / 0;
            return restTemplate.getForObject("http://29-nacos-discovery-provider/test", String.class);
            //受sentinel保护的代码 end
        }catch (BlockException e){
            e.printStackTrace();
            //手动写上服务降级的代码
            if (e instanceof FlowException) {
                return "接口限流了。。。。。。";
            } else if (e instanceof DegradeException) {
                return "服务降级了。。。。。。";
            } else if (e instanceof ParamFlowException) {
                return "热点参数限流了。。。。。。";
            } else if (e instanceof SystemBlockException) {
                return "触发系统保护规则。。。。。。";
            } else if (e instanceof AuthorityException) {
                return "授权规则不通过。。。。。。";
            }
            return "熔断了。。。。。。";
        }catch (ArithmeticException e){
            //对 int a = 10 / 0; 异常的监控
            Tracer.trace(e);
            return "除数不能为0";
        }finally {
            if (entry != null){
                entry.exit();
            }
            ContextUtil.exit();
        }
    }

簇点监控
设置流控规则
访问结果

采用注解@SentinelResource(value = “app”)实现埋点:

    /**
     * blockHandler = "block", blockHandlerClass = MyBlockHandlerClass.class 处理限流和降级
     * fallback = "fallback", fallbackClass = MyFallbackClass.class 处理限流和降级
     *
     * @param a
     * @param b
     * @return
     */
    @GetMapping("/app") // 埋点:加入sentinel的监控
    @SentinelResource(value = "app", fallback = "fallback", fallbackClass = MyFallbackClass.class)
//    @SentinelResource(value = "app", blockHandler = "block", blockHandlerClass = MyBlockHandlerClass.class)
    public String app(@RequestParam(value = "a", required = false) String a,
                      @RequestParam(value = "b", required = false) String b) {
        System.out.println("/app/--> " + a + "--" + b);
        return restTemplate.getForObject("http://29-nacos-discovery-provider/test", String.class);
    }

下面就介绍下@SentinelResource里面的方法:
SentinelResource

属性 作用 是否必须
value 资源名称(埋点名称)
entryType entry类型,标记流量的方向,取值IN或者OUT,默认OUT
blockHandler 处理BlockException的函数名称。函数要求:1. 必须是 public2.返回类型与原方法一致3. 参数类型需要和原方法相匹配,并在最后加 BlockException 类型的参数。4. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 blockHandlerClass ,并指定blockHandlerClass里面的方法
blockHandlerClass 存放blockHandler的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同blockHandler。
fallback 用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求:1. 返回类型与原方法一致2. 参数类型需要和原方法相匹配,Sentinel 1.6开始,也可在方法最后加 Throwable 类型的参数。3.默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定fallbackClass里面的方法
defaultFallback 用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求:1. 返回类型与原方法一致2. 方法参数列表为空,或者有一个 Throwable 类型的参数。3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定 fallbackClass 里面的方法。
fallbackClass 存放fallback的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同fallback。
exceptionsToIgnore 指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。
exceptionsToTrace 需要trace的异常 Throwable

Sentinel对RestTemplate模式调用和Feign模式调用流控熔断:

#true开启sentinel对resttemplate的支持,false则关闭
resttemplate.sentinel.enabled=true

  /**
     * ribbon负载均衡,默认是:ZoneAvoidanceRule
     * @return
     */
    @SentinelRestTemplate(blockHandler ="blockA" ,blockHandlerClass = MyBlockHandlerClass.class)    //服务限流
  //@SentinelRestTemplate(fallback = "fallbackA",fallbackClass = MyBlockHandlerClass.class)     //服务降级
    @LoadBalanced				//负载均衡
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
#true开启sentinel对feign的支持,false则关闭
feign.sentinel.enabled=true


@FeignClient(name = "29-nacos-discovery-provider")
        fallback = EchoServiceFallback.class,
//        fallbackFactory = EchoFeignServiceFallbackFactory.class,
        configuration = FeignConfiguration.class)
public interface EchoFeignService {

    @GetMapping("/echo/hello")
    default String hello() {
        return "hello";
    }

    @GetMapping("/echo/{str}")
    String echo(@PathVariable("str") String str);

    @GetMapping("/divide")
    String divide(@RequestParam("a") Integer a, @RequestParam("b") Integer b);
}    

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/77245.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!