【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

码农世界 2024-05-19 后端 68 次浏览 0个评论

文章目录

  • 推送模式
  • 本地文件持久化(拉模式)
    • 配置yml
    • 编写处理类
    • 添加配置
    • 演示
    • 配置中心持久化(推模式)
      • 修改nacos在sentinel中生效
        • 引入依赖
        • 配置文件
        • 修改sentinel在nacos中生效
          • 下载源码
          • 更改代码
          • 演示
          • 总结

            推送模式

            Sentinel 规则的推送有下面三种模式:

            【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

            通过前面的讲解,我们已经知道,可以通过 Dashboard 来为每个 Sentinel 客户端设置各种各样的规则,这种属于原始模式。这种模式存在一个问题,就是这些规则默认是存放在内存中的,极不稳定,所以需要将其持久化。

            【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

            为了达到持久化的目标,我们需要进行改造,改造的方案有两种:本地文件持久化(拉模式)、配置中心持久化(推模式)

            本地文件持久化(拉模式)

            拉模式又被称为 pull 模式,它的数据源(如本地文件、RDBMS等)一般是可写入的。本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。以本地文件数据源为例,推送过程如下图所示:

            【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

            首先 Sentinel 控制台通过API将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。使用 pull模式的数据源时一般不需要对Sentinel控制台进行改造。这种实现方法好处是简单,坏处是无法保证监控数据的一致性。

            配置yml

            #数据库配置
            spring:
              cloud:
                sentinel:
                  eager: true
                  transport:
                    port: 9998 #跟控制台交流的端口,随意指定一个未使用的端口即可
                    dashboard: localhost:8080  #指定控制台服务的地址
                  filter:
                    enabled: false
            

            编写处理类

            实现 InitFunc 接口,在 init 中处理 DataSource 初始化逻辑,并利用 SPI 机制实现加载。

            public class FilePersistence implements InitFunc {
                @Value("${spring.application.name}")
                private String applicationName;
                @Override
                public void init() throws Exception {
                    //创建规则文件
                    String ruleDir = System.getProperty("user.home") + "/sentinel-rules/" + applicationName;
                    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> flowRuleRDS = new FileRefreshableDataSource<>(flowRulePath,
                            source -> JSON.parseObject(source, new TypeReference>(){}));
                    //将可读数据源注册至 FlowRuleManager,这样当规则文件发生变化时,就会更新规则到内存
                    FlowRuleManager.register2Property(flowRuleRDS.getProperty());
                    WritableDataSource> flowRuleWDS = new FileWritableDataSource<>(flowRulePath,this::encodeJson);
                    //将可写数据源注册至 transport 模块的 WritableDataSourceRegistry 中.
                    //这样收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中.
                    WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
                    //降级规则
                    ReadableDataSource> degradeRuleRDS = new FileRefreshableDataSource<>(degradeRulePath,
                            source -> JSON.parseObject(source, new TypeReference>(){}));
                    DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
                    WritableDataSource> degradeRuleWDS = new FileWritableDataSource<>(degradeRulePath,this::encodeJson);
                    WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
                    //系统规则
                    ReadableDataSource> systemRuleRDS= new FileRefreshableDataSource<>(systemRulePath,
                            source -> JSON.parseObject(source, new TypeReference>(){}));
                    SystemRuleManager.register2Property(systemRuleRDS.getProperty());
                    WritableDataSource> systemRuleWDS = new FileWritableDataSource<>(systemRulePath,this::encodeJson);
                    WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
                    //授权规则
                    ReadableDataSource> authorityRuleRDS = new FileRefreshableDataSource<>(authorityRulePath,
                            source-> JSON.parseObject(source, new TypeReference>(){}));
                    AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
                    WritableDataSource> authorityRuleWDS = new FileWritableDataSource<>(authorityRulePath, this::encodeJson);
                    WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
                    //热点参数规则
                    ReadableDataSource> paramFlowRuleRDS = new FileRefreshableDataSource<>(paramFlowRulePath,
                            source-> JSON.parseObject(source, new TypeReference>(){}));
                    ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
                    WritableDataSource> paramFlowRuleWDS = new FileWritableDataSource<>(paramFlowRulePath,this::encodeJson);
                    ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
                }
            }
            
            • FileRefreshableDataSource:每次更新规则时自动读取持久化文件更新到map缓存。
            • FileWritableDataSource:写数据源,将 sentinel 控制台发送过来的规则信息写到持久化文件中。在客户端的socket接收到规则信息后,更新缓存的时候也会将规则信息写入文件中持久化。

              添加配置

              在resources下创建配置目录 META-INF/services,然后添加文件com.alibaba.csp.sentinel.init.InitFunc,在文件中添加配置类的全路径it.aq.cheetah.config.FilePersistence。

              这样当在 Dashboard 中修改了配置后,Dashboard 会调用客户端的接口修改客户端内存中的值,同时将配置写入文件中,这样操作的话规则是实时生效的,如果是直接修改文件中的内容,这样需要等定时任务3秒后执行才能读到最新的规则。接下来我们演示下:

              演示

              编写测试类

              @RestController
              @RequestMapping("/product2")
              @Slf4j
              public class ProductController2 {
                  @RequestMapping("/test")
                  @SentinelResource(value = "test")
                  public String test() {
                      return "product2";
                  }
              }
              

              启动项目,发现在目录下生成了空的规则文件

              【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

              在页面上增加流控规则

              【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

              然后去看文件flow-rule.json,发现存到了本地文件中

              【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

              接着我们仿照该规则仿写一个熔断规则,然后查看网页数据确实生效了

              【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

              配置中心持久化(推模式)

              推模式又叫 Push 模式,它是通过注册中心实现的,Sentinel控制台——>配置中心——>Sentinel数据源——>Sentinel

              【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

              用户不仅可以通过sentinel控制台进行更新,也可以通过nacos配置中心进行更新,所以在sentinel控制台或nacos中修改规则后,都需要通知对方刷新最新的配置。

              修改nacos在sentinel中生效

              引入依赖

              我们在之前项目的基础上引入新的依赖

              
              
                   com.alibaba.cloud
                   spring-cloud-starter-alibaba-nacos-config
               
               
               
                   com.alibaba.csp
                   sentinel-datasource-nacos
               
              

              配置文件

              nacos 配置:因为我们用nacos作为了配置中心,我们可以将sentinel的基本配置放入到nacos中就可以了,所以当前服务的yml配置文件中只需要写一些基本的配置就可以了。

              spring:
                cloud:
                  nacos:
                    discovery:
                      server-addr: localhost:8848
                    config:
                      server-addr: localhost:8848
                      file-extension: yml
              

              在nacos中配置sentinel信息

              spring: 
                cloud:
                  sentinel:
                    transport:
                      # 跟控制台交流的端口,随意指定一个未使用的端口即可
                      port: 9998 
                      # 指定控制台服务的地址
                      dashboard: localhost:8080  
                    # sentinel用nacos作为数据源的配置
                    datasource: 
                      #流控管理(这个名称可以自定义)
                      flow-control: 
                        # 告诉sentinel用nacos作为数据源
                        nacos: 
                          # 配置中心里执行文件的 dataId
                          dataId: shop-product-flow.json  
                          # nacos的地址
                          serverAddr: 127.0.0.1:8848 
                          # 指定文件配置的是哪种规则
                          rule-type: flow
              

              注意:如果使用的 namespace 不是默认的,记得配置 namespace 参数。

              • dataId:需要告诉 sentinel 读取配置中心中的哪个配置文件;
              • rule-type:告诉 sentinel 配置文件配置的控制规则,flow:流控、degrade:熔断、param-flow 热点参数,想看有哪些规则参数可以查看com.alibaba.cloud.sentinel.datasource包下的枚举类:RuleType。
                public enum RuleType {
                	/**
                	 * flow.
                	 */
                	FLOW("flow", FlowRule.class),
                	/**
                	 * degrade.
                	 */
                	DEGRADE("degrade", DegradeRule.class),
                	/**
                	 * param flow.
                	 */
                	PARAM_FLOW("param-flow", ParamFlowRule.class),
                	/**
                	 * system.
                	 */
                	SYSTEM("system", SystemRule.class),
                	/**
                	 * authority.
                	 */
                	AUTHORITY("authority", AuthorityRule.class),
                	/**
                	 * gateway flow.
                	 */
                	GW_FLOW("gw-flow",
                			"com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule"),
                	/**
                	 * api.
                	 */
                	GW_API_GROUP("gw-api-group",
                			"com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition");
                

                shop-product-flow.json文件中配置【流控规则】

                [
                	{
                		"clusterConfig": {
                			"acquireRefuseStrategy": 0,
                			"clientOfflineTime": 2000,
                			"fallbackToLocalWhenFail": true,
                			"resourceTimeout": 2000,
                			"resourceTimeoutStrategy": 0,
                			"sampleCount": 10,
                			"strategy": 0,
                			"thresholdType": 0,
                			"windowIntervalMs": 1000
                		},
                		"clusterMode": false,
                		"controlBehavior": 0,
                		"count": 10.0,
                		"grade": 1,
                		"limitApp": "default",
                		"maxQueueingTimeMs": 500,
                		"resource": "/product2/test",
                		"strategy": 0,
                		"warmUpPeriodSec": 10
                	}
                ]
                

                【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

                然后去 dashboard 中查看,发现流控规则已经在控制中显示了

                【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

                目前我们已经实现了在 nacos 中配置的文件直接在sentinel dashboard中生效,但是我们在sentinel dashboard中修改了配置,nacos 是不会监听到并进行修改的。接下来我们实现一下通过 sentinel 控制台设置的规则直接持久化到 nacos配置中心。

                修改sentinel在nacos中生效

                Sentinel 控制台提供 DynamicRulePublisher 和 DynamicRuleProvider 接口用于实现应用维度的规则推送和拉取。

                下载源码

                https://github.com/alibaba/Sentinel/releases下载dashboard的代码源码。

                【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

                解压之后打开sentinel-dashboard项目,将 pom.xml 文件中作用域为 test 的注释掉,注释掉后默认的作用域为 compile。

                • test:作用域表示该依赖项只在测试时有用,在编译和运行时不会被用到。
                • compile:作用域范围的依赖项在所有情况下都是有效的,包括编译、运行和测试。

                  把 test 包下的两个类复制过来

                  【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

                  • NacosConfigUtil 类主要就是 nacos 配置的规则,比如配置文件的后缀,分组Group_ID等等。因为我用的分组是默认分组,所以改为DEFAULT_GROUP,我之前的规则文件是shop-product-flow.json,所以我把规则文件后缀FLOW_DATA_ID_POSTFIX改为"-flow.json"。
                  • NacosConfig类是为了注入nacos的信息以及转换器类。

                    更改代码

                    application.properties 中增加 nacos 的配置

                    # nacos 配置
                    nacos.serverAddr=localhost:8848
                    nacos.username=nacos
                    nacos.password=nacos
                    

                    NacosConfig 修改为从配置文件中获取nacos配置

                     @Bean
                     public ConfigService nacosConfigService() throws Exception {
                         Properties properties = new Properties();
                         //Nacos地址
                         properties.put("serverAddr", serverAddr);
                         //Nacos用户名
                         properties.put("username", username);
                         //Nacos密码
                         properties.put("password", password);
                         return ConfigFactory.createConfigService(properties);
                     }
                    

                    在com.alibaba.csp.sentinel.dashboard.rule.FlowRuleApiPublisher#publish方法中增加推送到nacos的逻辑代码

                    //将规则推送到nacos
                    configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
                           NacosConfigUtil.GROUP_ID, converter.convert(rules));
                    

                    在com.alibaba.csp.sentinel.dashboard.rule.FlowRuleApiProvider#getRules方法中修改为从nacos中读取配置的逻辑

                    @Override
                    public List getRules(String appName) throws Exception {
                        String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
                                NacosConfigUtil.GROUP_ID, 3000);
                        if (StringUtil.isEmpty(rules)) {
                            return new ArrayList<>();
                        }
                        return converter.convert(rules);
                    }
                    

                    改造流控的controller类FlowControllerV1,将配置保存到内存中的逻辑改为保存到nacos中

                    @Autowired
                    @Qualifier("flowRuleDefaultProvider")
                    private DynamicRuleProvider> ruleProvider;
                    @Autowired
                    @Qualifier("flowRuleDefaultPublisher")
                    private DynamicRulePublisher> rulePublisher;
                    @GetMapping("/rules")
                    @AuthAction(PrivilegeType.READ_RULE)
                    public Result> apiQueryMachineRules(@RequestParam String app,
                              @RequestParam String ip,
                              @RequestParam Integer port) {
                        ......
                        try {
                    //            List rules = sentinelApiClient.fetchFlowRuleOfMachine(app, ip, port);
                            //从nacos中读取规则
                            List rules = ruleProvider.getRules(app);
                            rules = repository.saveAll(rules);
                            return Result.ofSuccess(rules);
                        } catch (Throwable throwable) {
                            logger.error("Error when querying flow rules", throwable);
                            return Result.ofThrowable(-1, throwable);
                        }
                    }
                    private void publishRules(String app, String ip, Integer port) throws Exception {
                          //将规则推送到nacos
                          List rules = repository.findAllByApp(app);
                          rulePublisher.publish(app, rules);
                    //        List rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
                    //        return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules);
                    }
                    //其余调用publishRules方法的地方做下简单调整
                    

                    演示

                    启动当前项目,流控规则中存在我们之前在nacos中创建的文件,我们将原来的单机阈值从10改为12,然后保存。查看nacos中配置文件的数据,发现已经生效了。

                    【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

                    至于其他规则,大家可以自行实现,此处就不一一实现了

                    【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式

                    总结

                    到这儿,服务容错中间件Sentinel的两种持久化模式就已经介绍完了。下一篇将为大家带来Feign整合容错组件 Sentinel 的文章,敬请期待吧!

                    后续的文章,我们将继续完善我们的微服务系统,集成更多的Alibaba组件。想要了解更多JAVA后端知识,请点击文末名片与我交流吧。留下您的一键三连,让我们在这个寒冷的东西互相温暖吧!

                    参考链接:

                    • https://blog.csdn.net/weixin_36279234/article/details/130922604;
                    • https://blog.csdn.net/u022812849/article/details/131206976;

转载请注明来自码农世界,本文标题:《【Spring Cloud】全面解析服务容错中间件 Sentinel 持久化两种模式》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,68人围观)参与讨论

还没有评论,来说两句吧...

Top