当前位置: 首页 > article >正文

SpringCloud系列教程(十四):Sentinel持久化

Sentinel之前已经搭建和应用成功了,但是它有一个很大的缺点就是官方没有提供持久化的方案,从项目源码上看感觉这款工具也没有完成的太好,所以需要我们去对它进行二次开发。要补充的功能大概如下:

1、将Sentinel接入nacos中,sentinel从nacos上进行参数读写。

2、使用sentinel的客户端开启nacos,可以读取nacos上关于sentinel的配置参数。

Sentinel接入Nacos实现读写功能。

1、下载sentinel的源码并且导入IDE,源码地址:GitHub - alibaba/Sentinel: A powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件)

2、找到项目中sentinel-dashboard这个module,我们就修改这个module下面的代码。

3、修改pom文件,源文件中sentinel-datasource-nacos这个依赖只用来做test,现在要加入到主功能中,去掉<scope>test</scope>。

        <dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId></dependency>

4、修改application.yaml文件,添加nacos的配置信息,之后的代码需要用到。

nacos.server-addr=192.168.3.54:80
nacos.namespace=sentinel-rules
nacos.group=devops
nacos.username=nacos
nacos.password=nacos

5、在package路径com.alibaba.csp.sentinel.dashboard.rule下创建子路径nacos,将sentinel接入nacos的功能写入到这个路径下。官方在test里面其实写了限流的配置,所以我也是参考官方代码完成的其他几个功能,这也就解释了为什么pom里的依赖是test的原因。

6、先创建一个工具类,提前写一些nacos的配置项参数。

package com.alibaba.csp.sentinel.dashboard.rule.nacos;public final class NacosConfigUtil {// Nacos分组ID 要和客户端一样
//    public static final String GROUP_ID = "devops";// 流控规则后缀 要和客户端一样public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";//熔断规则后缀 要和客户端一样public static final String DEGRADE_DATA_ID_POSTFIX = "-degrade-rules";//热点规则public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-flow-rules";//系统规则public static final String SYSTEM_DATA_ID_POSTFIX = "-system-rules";//授权规则public static final String AUTHORITY_DATA_ID_POSTFIX = "-authority-rules";public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";/*** cc for `cluster-client`*/public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config";/*** cs for `cluster-server`*/public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config";public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config";public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set";private NacosConfigUtil() {}
}

7、创建一个config,实现与nacos的连接。

package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.*;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.List;
import java.util.Properties;@Configuration
public class NacosConfig {@Value("${nacos.server-addr:localhost:8848}")private String serverAddr;@Value("${nacos.namespace:sentinel-rules}")private String namespace;@Value("${nacos.group:devops}")private String group;@Value("${nacos.username:nacos}")private String username;@Value("${nacos.password:nacos}")private String password;@Beanpublic Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<List<DegradeRuleEntity>, String> degradeRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<List<ParamFlowRuleEntity>, String> paramFlowRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<List<SystemRuleEntity>, String> systemRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<List<AuthorityRuleEntity>, String> authorityRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {return s -> JSON.parseArray(s, FlowRuleEntity.class);}@Beanpublic Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() {return s -> JSON.parseArray(s, DegradeRuleEntity.class);}@Beanpublic Converter<String, List<ParamFlowRuleEntity>> paramFlowRuleEntityDecoder() {return s -> JSON.parseArray(s, ParamFlowRuleEntity.class);}@Beanpublic Converter<String, List<SystemRuleEntity>> systemRuleEntityDecoder() {return s -> JSON.parseArray(s, SystemRuleEntity.class);}@Beanpublic Converter<String, List<AuthorityRuleEntity>> authorityRuleEntityDecoder() {return s -> JSON.parseArray(s, AuthorityRuleEntity.class);}@Beanpublic ConfigService nacosConfigService() throws Exception {// 配置注册中心 和 namespaceProperties properties = new Properties();properties.put(PropertyKeyConst.SERVER_ADDR, this.serverAddr);properties.put(PropertyKeyConst.NAMESPACE, this.namespace); // 命名空间,要和客户端配置的一样properties.put(PropertyKeyConst.USERNAME, this.username);properties.put(PropertyKeyConst.PASSWORD, this.password);return ConfigFactory.createConfigService(properties);}
}

8、创建限流功能的一对功能文件:FlowRuleNacosProvider和FlowRuleNacosPublisher,FlowRuleNacosProvider就是让sentinel从nacos上获取配置,我们默认就用json格式,所以在上面的NacosConfig文件中有json字符串转对象的多个方法,FlowRuleNacosPublisher就是sentinel页面修改配置之后更新nacos。

package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<FlowRuleEntity>> converter;@Value("${nacos.group:devops}")private String group;@Overridepublic List<FlowRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX, group, 3000);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}

package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.List;@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<FlowRuleEntity>, String> converter;@Value("${nacos.group:devops}")private String group;@Overridepublic void publish(String app, List<FlowRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX, group, converter.convert(rules), ConfigType.JSON.getType());}
}

9、熔断也是两个文件,DegradeRuleNacosProvider和DegradeRuleNacosPublisher,就是仿照限流两个文件写的,里面改改参数即可,下面3个功能也一样。

package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;@Component("degradeRuleNacosProvider")
public class DegradeRuleNacosProvider implements DynamicRuleProvider<List<DegradeRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<DegradeRuleEntity>> converter;@Value("${nacos.group:devops}")private String group;@Overridepublic List<DegradeRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX, group, 3000);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}
package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.List;@Component("degradeRuleNacosPublisher")
public class DegradeRuleNacosPublisher implements DynamicRulePublisher<List<DegradeRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<DegradeRuleEntity>, String> converter;@Value("${nacos.group:devops}")private String group;@Overridepublic void publish(String app, List<DegradeRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}configService.publishConfig(app + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX, group, converter.convert(rules), ConfigType.JSON.getType());}
}

10、热点规则

package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;@Component("paramFlowRuleNacosProvider")
public class ParamFlowRuleNacosProvider implements DynamicRuleProvider<List<ParamFlowRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<ParamFlowRuleEntity>> converter;@Value("${nacos.group:devops}")private String group;@Overridepublic List<ParamFlowRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX, group, 3000);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}
package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.List;@Component("paramFlowRuleNacosPublisher")
public class ParamFlowRuleNacosPublisher implements DynamicRulePublisher<List<ParamFlowRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<ParamFlowRuleEntity>, String> converter;@Value("${nacos.group:devops}")private String group;@Overridepublic void publish(String app, List<ParamFlowRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}configService.publishConfig(app + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX, group, converter.convert(rules), ConfigType.JSON.getType());}
}

11、系统规则

package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;@Component("systemRuleNacosProvider")
public class SystemRuleNacosProvider implements DynamicRuleProvider<List<SystemRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<SystemRuleEntity>> converter;@Value("${nacos.group:devops}")private String group;@Overridepublic List<SystemRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.SYSTEM_DATA_ID_POSTFIX, group, 3000);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}
package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.List;@Component("systemRuleNacosPublisher")
public class SystemRuleNacosPublisher implements DynamicRulePublisher<List<SystemRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<SystemRuleEntity>, String> converter;@Value("${nacos.group:devops}")private String group;@Overridepublic void publish(String app, List<SystemRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}configService.publishConfig(app + NacosConfigUtil.SYSTEM_DATA_ID_POSTFIX, group, converter.convert(rules), ConfigType.JSON.getType());}
}

12、授权规则

package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;@Component("authorityRuleNacosProvider")
public class AuthorityRuleNacosProvider implements DynamicRuleProvider<List<AuthorityRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<AuthorityRuleEntity>> converter;@Value("${nacos.group:devops}")private String group;@Overridepublic List<AuthorityRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.AUTHORITY_DATA_ID_POSTFIX, group, 3000);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}
}
package com.alibaba.csp.sentinel.dashboard.rule.nacos;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.List;@Component("authorityRuleNacosPublisher")
public class AuthorityRuleNacosPublisher implements DynamicRulePublisher<List<AuthorityRuleEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<AuthorityRuleEntity>, String> converter;@Value("${nacos.group:devops}")private String group;@Overridepublic void publish(String app, List<AuthorityRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}configService.publishConfig(app + NacosConfigUtil.AUTHORITY_DATA_ID_POSTFIX, group, converter.convert(rules), ConfigType.JSON.getType());}
}

13、还要修改Controller,因为web页面调用的时候要改成我们上面做的这些收发功能,限流这个Controller已经有了,但是有两个,我们用V1这个,有些教程用的V2,我建议用V1,因为官方还没有正式使用V2,而且这个工具更新不是太积极。

主要修改就是把com.alibaba.csp.sentinel.dashboard.controller.FlowControllerV1中的SentinelApiClient 替换成DynamicRuleProvider和DynamicRulePublisher,这里改动并不大,我直接贴出来更新后的文件做参考。

/** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.alibaba.csp.sentinel.dashboard.controller;import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;@RestController
@RequestMapping(value = "/v1/flow")
public class FlowControllerV1 {private final Logger logger = LoggerFactory.getLogger(FlowControllerV1.class);@Autowiredprivate InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;@Autowired
//    @Qualifier("flowRuleDefaultProvider")@Qualifier("flowRuleNacosProvider")private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;@Autowired
//    @Qualifier("flowRuleDefaultPublisher")@Qualifier("flowRuleNacosPublisher")private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;@GetMapping("/rules")@AuthAction(PrivilegeType.READ_RULE)public Result<List<FlowRuleEntity>> apiQueryMachineRules(@RequestParam String app) {if (StringUtil.isEmpty(app)) {return Result.ofFail(-1, "app can't be null or empty");}try {List<FlowRuleEntity> rules = ruleProvider.getRules(app);if (rules != null && !rules.isEmpty()) {for (FlowRuleEntity entity : rules) {entity.setApp(app);if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) {entity.setId(entity.getClusterConfig().getFlowId());}}}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 <R> Result<R> checkEntityInternal(FlowRuleEntity entity) {if (entity == null) {return Result.ofFail(-1, "invalid body");}if (StringUtil.isBlank(entity.getApp())) {return Result.ofFail(-1, "app can't be null or empty");}if (StringUtil.isBlank(entity.getLimitApp())) {return Result.ofFail(-1, "limitApp can't be null or empty");}if (StringUtil.isBlank(entity.getResource())) {return Result.ofFail(-1, "resource can't be null or empty");}if (entity.getGrade() == null) {return Result.ofFail(-1, "grade can't be null");}if (entity.getGrade() != 0 && entity.getGrade() != 1) {return Result.ofFail(-1, "grade must be 0 or 1, but " + entity.getGrade() + " got");}if (entity.getCount() == null || entity.getCount() < 0) {return Result.ofFail(-1, "count should be at lease zero");}if (entity.getStrategy() == null) {return Result.ofFail(-1, "strategy can't be null");}if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) {return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0");}if (entity.getControlBehavior() == null) {return Result.ofFail(-1, "controlBehavior can't be null");}int controlBehavior = entity.getControlBehavior();if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) {return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1");}if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) {return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2");}if (entity.isClusterMode() && entity.getClusterConfig() == null) {return Result.ofFail(-1, "cluster config should be valid");}return null;}@PostMapping("/rule")@AuthAction(value = PrivilegeType.WRITE_RULE)public Result<FlowRuleEntity> apiAddFlowRule(@RequestBody FlowRuleEntity entity) {Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);if (checkResult != null) {return checkResult;}entity.setId(null);Date date = new Date();entity.setGmtCreate(date);entity.setGmtModified(date);entity.setLimitApp(entity.getLimitApp().trim());entity.setResource(entity.getResource().trim());try {entity = repository.save(entity);publishRules(entity.getApp());} catch (Throwable throwable) {logger.error("Failed to add flow rule", throwable);return Result.ofThrowable(-1, throwable);}return Result.ofSuccess(entity);}@PutMapping("/save.json")@AuthAction(PrivilegeType.WRITE_RULE)public Result<FlowRuleEntity> apiUpdateFlowRule(Long id, String app,String limitApp, String resource, Integer grade,Double count, Integer strategy, String refResource,Integer controlBehavior, Integer warmUpPeriodSec,Integer maxQueueingTimeMs) {if (id == null) {return Result.ofFail(-1, "id can't be null");}FlowRuleEntity entity = repository.findById(id);if (entity == null) {return Result.ofFail(-1, "id " + id + " dose not exist");}if (StringUtil.isNotBlank(app)) {entity.setApp(app.trim());}if (StringUtil.isNotBlank(limitApp)) {entity.setLimitApp(limitApp.trim());}if (StringUtil.isNotBlank(resource)) {entity.setResource(resource.trim());}if (grade != null) {if (grade != 0 && grade != 1) {return Result.ofFail(-1, "grade must be 0 or 1, but " + grade + " got");}entity.setGrade(grade);}if (count != null) {entity.setCount(count);}if (strategy != null) {if (strategy != 0 && strategy != 1 && strategy != 2) {return Result.ofFail(-1, "strategy must be in [0, 1, 2], but " + strategy + " got");}entity.setStrategy(strategy);if (strategy != 0) {if (StringUtil.isBlank(refResource)) {return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0");}entity.setRefResource(refResource.trim());}}if (controlBehavior != null) {if (controlBehavior != 0 && controlBehavior != 1 && controlBehavior != 2) {return Result.ofFail(-1, "controlBehavior must be in [0, 1, 2], but " + controlBehavior + " got");}if (controlBehavior == 1 && warmUpPeriodSec == null) {return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1");}if (controlBehavior == 2 && maxQueueingTimeMs == null) {return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2");}entity.setControlBehavior(controlBehavior);if (warmUpPeriodSec != null) {entity.setWarmUpPeriodSec(warmUpPeriodSec);}if (maxQueueingTimeMs != null) {entity.setMaxQueueingTimeMs(maxQueueingTimeMs);}}Date date = new Date();entity.setGmtModified(date);try {entity = repository.save(entity);if (entity == null) {return Result.ofFail(-1, "save entity fail: null");}publishRules(entity.getApp());return Result.ofSuccess(entity);} catch (Throwable t) {Throwable e = t instanceof ExecutionException ? t.getCause() : t;logger.error("Error when updating flow rules, app={}, ip={}, ruleId={}", entity.getApp(),entity.getIp(), id, e);return Result.ofFail(-1, e.getMessage());}}@DeleteMapping("/delete.json")@AuthAction(PrivilegeType.WRITE_RULE)public Result<Long> apiDeleteFlowRule(Long id) {if (id == null) {return Result.ofFail(-1, "id can't be null");}FlowRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofSuccess(null);}try {repository.delete(id);} catch (Exception e) {return Result.ofFail(-1, e.getMessage());}try {publishRules(oldEntity.getApp());return Result.ofSuccess(id);} catch (Throwable t) {Throwable e = t instanceof ExecutionException ? t.getCause() : t;logger.error("Error when deleting flow rules, app={}, ip={}, id={}", oldEntity.getApp(),oldEntity.getIp(), id, e);return Result.ofFail(-1, e.getMessage());}}private void publishRules(/*@NonNull*/ String app) throws Exception {List<FlowRuleEntity> rules = repository.findAllByApp(app);rulePublisher.publish(app, rules);}
}

14、剩下4个功能都只有1个Controller,也是一样改法。

/** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.alibaba.csp.sentinel.dashboard.controller;import java.util.Date;
import java.util.List;
import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
import com.alibaba.csp.sentinel.util.StringUtil;import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.domain.Result;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/degrade")
public class DegradeController {private final Logger logger = LoggerFactory.getLogger(DegradeController.class);@Autowiredprivate RuleRepository<DegradeRuleEntity, Long> repository;@Autowiredprivate SentinelApiClient sentinelApiClient;@Autowiredprivate AppManagement appManagement;@Autowired@Qualifier("degradeRuleNacosProvider")private DynamicRuleProvider<List<DegradeRuleEntity>> ruleProvider;@Autowired@Qualifier("degradeRuleNacosPublisher")private DynamicRulePublisher<List<DegradeRuleEntity>> rulePublisher;@GetMapping("/rules.json")@AuthAction(PrivilegeType.READ_RULE)public Result<List<DegradeRuleEntity>> apiQueryMachineRules(String app, String ip, Integer port) {if (StringUtil.isEmpty(app)) {return Result.ofFail(-1, "app can't be null or empty");}if (StringUtil.isEmpty(ip)) {return Result.ofFail(-1, "ip can't be null or empty");}if (port == null) {return Result.ofFail(-1, "port can't be null");}try {//List<DegradeRuleEntity> rules = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port);List<DegradeRuleEntity> rules = ruleProvider.getRules(app);rules = repository.saveAll(rules);return Result.ofSuccess(rules);} catch (Throwable throwable) {logger.error("queryApps error:", throwable);return Result.ofThrowable(-1, throwable);}}@PostMapping("/rule")@AuthAction(PrivilegeType.WRITE_RULE)public Result<DegradeRuleEntity> apiAddRule(@RequestBody DegradeRuleEntity entity) {Result<DegradeRuleEntity> checkResult = checkEntityInternal(entity);if (checkResult != null) {return checkResult;}Date date = new Date();entity.setGmtCreate(date);entity.setGmtModified(date);try {entity = repository.save(entity);} catch (Throwable t) {logger.error("Failed to add new degrade rule, app={}, ip={}", entity.getApp(), entity.getIp(), t);return Result.ofThrowable(-1, t);}try {publishRules(entity.getApp());} catch (Throwable throwable) {logger.error("Failed to add degrade rule", throwable);return Result.ofThrowable(-1, throwable);}return Result.ofSuccess(entity);}@PutMapping("/rule/{id}")@AuthAction(PrivilegeType.WRITE_RULE)public Result<DegradeRuleEntity> apiUpdateRule(@PathVariable("id") Long id,@RequestBody DegradeRuleEntity entity) {if (id == null || id <= 0) {return Result.ofFail(-1, "id can't be null or negative");}DegradeRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofFail(-1, "Degrade rule does not exist, id=" + id);}entity.setApp(oldEntity.getApp());entity.setIp(oldEntity.getIp());entity.setPort(oldEntity.getPort());entity.setId(oldEntity.getId());Result<DegradeRuleEntity> checkResult = checkEntityInternal(entity);if (checkResult != null) {return checkResult;}entity.setGmtCreate(oldEntity.getGmtCreate());entity.setGmtModified(new Date());try {entity = repository.save(entity);} catch (Throwable t) {logger.error("Failed to save degrade rule, id={}, rule={}", id, entity, t);return Result.ofThrowable(-1, t);}try {publishRules(entity.getApp());} catch (Throwable throwable) {logger.error("Failed to update degrade rule", throwable);return Result.ofThrowable(-1, throwable);}return Result.ofSuccess(entity);}@DeleteMapping("/rule/{id}")@AuthAction(PrivilegeType.DELETE_RULE)public Result<Long> delete(@PathVariable("id") Long id) {if (id == null) {return Result.ofFail(-1, "id can't be null");}DegradeRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofSuccess(null);}try {repository.delete(id);} catch (Throwable throwable) {logger.error("Failed to delete degrade rule, id={}", id, throwable);return Result.ofThrowable(-1, throwable);}try {publishRules(oldEntity.getApp());} catch (Throwable throwable) {logger.error("Failed to add delete rule", throwable);return Result.ofThrowable(-1, throwable);}return Result.ofSuccess(id);}//    private boolean publishRules(String app, String ip, Integer port) {
//        List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
//        return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules);
//    }private void publishRules(/*@NonNull*/ String app) throws Exception {List<DegradeRuleEntity> rules = repository.findAllByApp(app);rulePublisher.publish(app, rules);}private <R> Result<R> checkEntityInternal(DegradeRuleEntity entity) {if (StringUtil.isBlank(entity.getApp())) {return Result.ofFail(-1, "app can't be blank");}
//        if (StringUtil.isBlank(entity.getIp())) {
//            return Result.ofFail(-1, "ip can't be null or empty");
//        }
//        if (entity.getPort() == null || entity.getPort() <= 0) {
//            return Result.ofFail(-1, "invalid port: " + entity.getPort());
//        }if (StringUtil.isBlank(entity.getLimitApp())) {entity.setLimitApp("default");
//            return Result.ofFail(-1, "limitApp can't be null or empty");}if (StringUtil.isBlank(entity.getResource())) {return Result.ofFail(-1, "resource can't be null or empty");}Double threshold = entity.getCount();if (threshold == null || threshold < 0) {return Result.ofFail(-1, "invalid threshold: " + threshold);}Integer recoveryTimeoutSec = entity.getTimeWindow();if (recoveryTimeoutSec == null || recoveryTimeoutSec <= 0) {return Result.ofFail(-1, "recoveryTimeout should be positive");}Integer strategy = entity.getGrade();if (strategy == null) {return Result.ofFail(-1, "circuit breaker strategy cannot be null");}if (strategy < CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType()|| strategy > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {return Result.ofFail(-1, "Invalid circuit breaker strategy: " + strategy);}if (entity.getMinRequestAmount()  == null || entity.getMinRequestAmount() <= 0) {return Result.ofFail(-1, "Invalid minRequestAmount");}if (entity.getStatIntervalMs() == null || entity.getStatIntervalMs() <= 0) {return Result.ofFail(-1, "Invalid statInterval");}if (strategy == RuleConstant.DEGRADE_GRADE_RT) {Double slowRatio = entity.getSlowRatioThreshold();if (slowRatio == null) {return Result.ofFail(-1, "SlowRatioThreshold is required for slow request ratio strategy");} else if (slowRatio < 0 || slowRatio > 1) {return Result.ofFail(-1, "SlowRatioThreshold should be in range: [0.0, 1.0]");}} else if (strategy == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) {if (threshold > 1) {return Result.ofFail(-1, "Ratio threshold should be in range: [0.0, 1.0]");}}return null;}
}
/** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.alibaba.csp.sentinel.dashboard.controller;import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.util.VersionUtils;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;/*** @author Eric Zhao* @since 0.2.1*/
@RestController
@RequestMapping(value = "/paramFlow")
public class ParamFlowRuleController {private final Logger logger = LoggerFactory.getLogger(ParamFlowRuleController.class);@Autowiredprivate SentinelApiClient sentinelApiClient;@Autowiredprivate AppManagement appManagement;@Autowiredprivate RuleRepository<ParamFlowRuleEntity, Long> repository;@Autowired@Qualifier("paramFlowRuleNacosProvider")private DynamicRuleProvider<List<ParamFlowRuleEntity>> ruleProvider;@Autowired@Qualifier("paramFlowRuleNacosPublisher")private DynamicRulePublisher<List<ParamFlowRuleEntity>> rulePublisher;private boolean checkIfSupported(String app, String ip, int port) {try {return Optional.ofNullable(appManagement.getDetailApp(app)).flatMap(e -> e.getMachine(ip, port)).flatMap(m -> VersionUtils.parseVersion(m.getVersion()).map(v -> v.greaterOrEqual(version020))).orElse(true);// If error occurred or cannot retrieve machine info, return true.} catch (Exception ex) {return true;}}@GetMapping("/rules")@AuthAction(PrivilegeType.READ_RULE)public Result<List<ParamFlowRuleEntity>> apiQueryAllRulesForMachine(@RequestParam String app,@RequestParam String ip,@RequestParam Integer port) {if (StringUtil.isEmpty(app)) {return Result.ofFail(-1, "app cannot be null or empty");}if (StringUtil.isEmpty(ip)) {return Result.ofFail(-1, "ip cannot be null or empty");}if (port == null || port <= 0) {return Result.ofFail(-1, "Invalid parameter: port");}if (!appManagement.isValidMachineOfApp(app, ip)) {return Result.ofFail(-1, "given ip does not belong to given app");}if (!checkIfSupported(app, ip, port)) {return unsupportedVersion();}try {
//            return sentinelApiClient.fetchParamFlowRulesOfMachine(app, ip, port)
//                .thenApply(repository::saveAll)
//                .thenApply(Result::ofSuccess)
//                .get();List<ParamFlowRuleEntity> rules = ruleProvider.getRules(app);rules = repository.saveAll(rules);return Result.ofSuccess(rules);} catch (ExecutionException ex) {logger.error("Error when querying parameter flow rules", ex.getCause());if (isNotSupported(ex.getCause())) {return unsupportedVersion();} else {return Result.ofThrowable(-1, ex.getCause());}} catch (Throwable throwable) {logger.error("Error when querying parameter flow rules", throwable);return Result.ofFail(-1, throwable.getMessage());}}private boolean isNotSupported(Throwable ex) {return ex instanceof CommandNotFoundException;}@PostMapping("/rule")@AuthAction(AuthService.PrivilegeType.WRITE_RULE)public Result<ParamFlowRuleEntity> apiAddParamFlowRule(@RequestBody ParamFlowRuleEntity entity) {Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);if (checkResult != null) {return checkResult;}if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) {return unsupportedVersion();}entity.setId(null);entity.getRule().setResource(entity.getResource().trim());Date date = new Date();entity.setGmtCreate(date);entity.setGmtModified(date);try {entity = repository.save(entity);
//            publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get();publishRules(entity.getApp());return Result.ofSuccess(entity);} catch (ExecutionException ex) {logger.error("Error when adding new parameter flow rules", ex.getCause());if (isNotSupported(ex.getCause())) {return unsupportedVersion();} else {return Result.ofThrowable(-1, ex.getCause());}} catch (Throwable throwable) {logger.error("Error when adding new parameter flow rules", throwable);return Result.ofFail(-1, throwable.getMessage());}}private <R> Result<R> checkEntityInternal(ParamFlowRuleEntity entity) {if (entity == null) {return Result.ofFail(-1, "bad rule body");}if (StringUtil.isBlank(entity.getApp())) {return Result.ofFail(-1, "app can't be null or empty");}if (StringUtil.isBlank(entity.getIp())) {return Result.ofFail(-1, "ip can't be null or empty");}if (entity.getPort() == null || entity.getPort() <= 0) {return Result.ofFail(-1, "port can't be null");}if (entity.getRule() == null) {return Result.ofFail(-1, "rule can't be null");}if (StringUtil.isBlank(entity.getResource())) {return Result.ofFail(-1, "resource name cannot be null or empty");}if (entity.getCount() < 0) {return Result.ofFail(-1, "count should be valid");}if (entity.getGrade() != RuleConstant.FLOW_GRADE_QPS) {return Result.ofFail(-1, "Unknown mode (blockGrade) for parameter flow control");}if (entity.getParamIdx() == null || entity.getParamIdx() < 0) {return Result.ofFail(-1, "paramIdx should be valid");}if (entity.getDurationInSec() <= 0) {return Result.ofFail(-1, "durationInSec should be valid");}if (entity.getControlBehavior() < 0) {return Result.ofFail(-1, "controlBehavior should be valid");}return null;}@PutMapping("/rule/{id}")@AuthAction(AuthService.PrivilegeType.WRITE_RULE)public Result<ParamFlowRuleEntity> apiUpdateParamFlowRule(@PathVariable("id") Long id,@RequestBody ParamFlowRuleEntity entity) {if (id == null || id <= 0) {return Result.ofFail(-1, "Invalid id");}ParamFlowRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofFail(-1, "id " + id + " does not exist");}Result<ParamFlowRuleEntity> checkResult = checkEntityInternal(entity);if (checkResult != null) {return checkResult;}if (!checkIfSupported(entity.getApp(), entity.getIp(), entity.getPort())) {return unsupportedVersion();}entity.setId(id);Date date = new Date();entity.setGmtCreate(oldEntity.getGmtCreate());entity.setGmtModified(date);try {entity = repository.save(entity);
//            publishRules(entity.getApp(), entity.getIp(), entity.getPort()).get();publishRules(entity.getApp());return Result.ofSuccess(entity);} catch (ExecutionException ex) {logger.error("Error when updating parameter flow rules, id=" + id, ex.getCause());if (isNotSupported(ex.getCause())) {return unsupportedVersion();} else {return Result.ofThrowable(-1, ex.getCause());}} catch (Throwable throwable) {logger.error("Error when updating parameter flow rules, id=" + id, throwable);return Result.ofFail(-1, throwable.getMessage());}}@DeleteMapping("/rule/{id}")@AuthAction(PrivilegeType.DELETE_RULE)public Result<Long> apiDeleteRule(@PathVariable("id") Long id) {if (id == null) {return Result.ofFail(-1, "id cannot be null");}ParamFlowRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofSuccess(null);}try {repository.delete(id);
//            publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort()).get();publishRules(oldEntity.getApp());return Result.ofSuccess(id);} catch (ExecutionException ex) {logger.error("Error when deleting parameter flow rules", ex.getCause());if (isNotSupported(ex.getCause())) {return unsupportedVersion();} else {return Result.ofThrowable(-1, ex.getCause());}} catch (Throwable throwable) {logger.error("Error when deleting parameter flow rules", throwable);return Result.ofFail(-1, throwable.getMessage());}}//    private CompletableFuture<Void> publishRules(String app, String ip, Integer port) {
//        List<ParamFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
//        return sentinelApiClient.setParamFlowRuleOfMachine(app, ip, port, rules);
//    }private void publishRules(/*@NonNull*/ String app) throws Exception {List<ParamFlowRuleEntity> rules = repository.findAllByApp(app);rulePublisher.publish(app, rules);}private <R> Result<R> unsupportedVersion() {return Result.ofFail(4041,"Sentinel client not supported for parameter flow control (unsupported version or dependency absent)");}private final SentinelVersion version020 = new SentinelVersion().setMinorVersion(2);
}
/** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.alibaba.csp.sentinel.dashboard.controller;import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.SystemRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.Date;
import java.util.List;/*** @author leyou(lihao)*/
@RestController
@RequestMapping("/system")
public class SystemController {private final Logger logger = LoggerFactory.getLogger(SystemController.class);@Autowiredprivate RuleRepository<SystemRuleEntity, Long> repository;@Autowiredprivate SentinelApiClient sentinelApiClient;@Autowiredprivate AppManagement appManagement;@Autowired@Qualifier("systemRuleNacosProvider")private DynamicRuleProvider<List<SystemRuleEntity>> ruleProvider;@Autowired@Qualifier("systemRuleNacosPublisher")private DynamicRulePublisher<List<SystemRuleEntity>> rulePublisher;private <R> Result<R> checkBasicParams(String app, String ip, Integer port) {if (StringUtil.isEmpty(app)) {return Result.ofFail(-1, "app can't be null or empty");}if (StringUtil.isEmpty(ip)) {return Result.ofFail(-1, "ip can't be null or empty");}if (port == null) {return Result.ofFail(-1, "port can't be null");}if (!appManagement.isValidMachineOfApp(app, ip)) {return Result.ofFail(-1, "given ip does not belong to given app");}if (port <= 0 || port > 65535) {return Result.ofFail(-1, "port should be in (0, 65535)");}return null;}@GetMapping("/rules.json")@AuthAction(PrivilegeType.READ_RULE)public Result<List<SystemRuleEntity>> apiQueryMachineRules(String app, String ip,Integer port) {Result<List<SystemRuleEntity>> checkResult = checkBasicParams(app, ip, port);if (checkResult != null) {return checkResult;}try {
//            List<SystemRuleEntity> rules = sentinelApiClient.fetchSystemRuleOfMachine(app, ip, port);List<SystemRuleEntity> rules = ruleProvider.getRules(app);rules = repository.saveAll(rules);return Result.ofSuccess(rules);} catch (Throwable throwable) {logger.error("Query machine system rules error", throwable);return Result.ofThrowable(-1, throwable);}}private int countNotNullAndNotNegative(Number... values) {int notNullCount = 0;for (int i = 0; i < values.length; i++) {if (values[i] != null && values[i].doubleValue() >= 0) {notNullCount++;}}return notNullCount;}@RequestMapping("/new.json")@AuthAction(PrivilegeType.WRITE_RULE)public Result<SystemRuleEntity> apiAdd(String app, String ip, Integer port,Double highestSystemLoad, Double highestCpuUsage, Long avgRt,Long maxThread, Double qps) {Result<SystemRuleEntity> checkResult = checkBasicParams(app, ip, port);if (checkResult != null) {return checkResult;}int notNullCount = countNotNullAndNotNegative(highestSystemLoad, avgRt, maxThread, qps, highestCpuUsage);if (notNullCount != 1) {return Result.ofFail(-1, "only one of [highestSystemLoad, avgRt, maxThread, qps,highestCpuUsage] "+ "value must be set > 0, but " + notNullCount + " values get");}if (null != highestCpuUsage && highestCpuUsage > 1) {return Result.ofFail(-1, "highestCpuUsage must between [0.0, 1.0]");}SystemRuleEntity entity = new SystemRuleEntity();entity.setApp(app.trim());entity.setIp(ip.trim());entity.setPort(port);// -1 is a fake valueif (null != highestSystemLoad) {entity.setHighestSystemLoad(highestSystemLoad);} else {entity.setHighestSystemLoad(-1D);}if (null != highestCpuUsage) {entity.setHighestCpuUsage(highestCpuUsage);} else {entity.setHighestCpuUsage(-1D);}if (avgRt != null) {entity.setAvgRt(avgRt);} else {entity.setAvgRt(-1L);}if (maxThread != null) {entity.setMaxThread(maxThread);} else {entity.setMaxThread(-1L);}if (qps != null) {entity.setQps(qps);} else {entity.setQps(-1D);}Date date = new Date();entity.setGmtCreate(date);entity.setGmtModified(date);try {entity = repository.save(entity);} catch (Throwable throwable) {logger.error("Add SystemRule error", throwable);return Result.ofThrowable(-1, throwable);}
//        if (!publishRules(app, ip, port)) {
//            logger.warn("Publish system rules fail after rule add");
//        }try {publishRules(entity.getApp());} catch (Throwable throwable) {logger.error("Publish system rules fail after rule add", throwable);return Result.ofThrowable(-1, throwable);}return Result.ofSuccess(entity);}@GetMapping("/save.json")@AuthAction(PrivilegeType.WRITE_RULE)public Result<SystemRuleEntity> apiUpdateIfNotNull(Long id, String app, Double highestSystemLoad,Double highestCpuUsage, Long avgRt, Long maxThread, Double qps) {if (id == null) {return Result.ofFail(-1, "id can't be null");}SystemRuleEntity entity = repository.findById(id);if (entity == null) {return Result.ofFail(-1, "id " + id + " dose not exist");}if (StringUtil.isNotBlank(app)) {entity.setApp(app.trim());}if (highestSystemLoad != null) {if (highestSystemLoad < 0) {return Result.ofFail(-1, "highestSystemLoad must >= 0");}entity.setHighestSystemLoad(highestSystemLoad);}if (highestCpuUsage != null) {if (highestCpuUsage < 0) {return Result.ofFail(-1, "highestCpuUsage must >= 0");}if (highestCpuUsage > 1) {return Result.ofFail(-1, "highestCpuUsage must <= 1");}entity.setHighestCpuUsage(highestCpuUsage);}if (avgRt != null) {if (avgRt < 0) {return Result.ofFail(-1, "avgRt must >= 0");}entity.setAvgRt(avgRt);}if (maxThread != null) {if (maxThread < 0) {return Result.ofFail(-1, "maxThread must >= 0");}entity.setMaxThread(maxThread);}if (qps != null) {if (qps < 0) {return Result.ofFail(-1, "qps must >= 0");}entity.setQps(qps);}Date date = new Date();entity.setGmtModified(date);try {entity = repository.save(entity);} catch (Throwable throwable) {logger.error("save error:", throwable);return Result.ofThrowable(-1, throwable);}
//        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
//            logger.info("publish system rules fail after rule update");
//        }try {publishRules(entity.getApp());} catch (Throwable throwable) {logger.error("publish system rules fail after rule update", throwable);return Result.ofThrowable(-1, throwable);}return Result.ofSuccess(entity);}@RequestMapping("/delete.json")@AuthAction(PrivilegeType.DELETE_RULE)public Result<?> delete(Long id) {if (id == null) {return Result.ofFail(-1, "id can't be null");}SystemRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofSuccess(null);}try {repository.delete(id);} catch (Throwable throwable) {logger.error("delete error:", throwable);return Result.ofThrowable(-1, throwable);}
//        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
//            logger.info("publish system rules fail after rule delete");
//        }try {publishRules(oldEntity.getApp());} catch (Throwable throwable) {logger.error("publish system rules fail after rule delete", throwable);return Result.ofThrowable(-1, throwable);}return Result.ofSuccess(id);}//    private boolean publishRules(String app, String ip, Integer port) {
//        List<SystemRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
//        return sentinelApiClient.setSystemRuleOfMachine(app, ip, port, rules);
//    }private void publishRules(/*@NonNull*/ String app) throws Exception {List<SystemRuleEntity> rules = repository.findAllByApp(app);rulePublisher.publish(app, rules);}
}
/** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.alibaba.csp.sentinel.dashboard.controller;import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;import java.util.Date;
import java.util.List;/*** @author Eric Zhao* @since 0.2.1*/
@RestController
@RequestMapping(value = "/authority")
public class AuthorityRuleController {private final Logger logger = LoggerFactory.getLogger(AuthorityRuleController.class);@Autowiredprivate SentinelApiClient sentinelApiClient;@Autowiredprivate RuleRepository<AuthorityRuleEntity, Long> repository;@Autowiredprivate AppManagement appManagement;@Autowired@Qualifier("authorityRuleNacosProvider")private DynamicRuleProvider<List<AuthorityRuleEntity>> ruleProvider;@Autowired@Qualifier("authorityRuleNacosPublisher")private DynamicRulePublisher<List<AuthorityRuleEntity>> rulePublisher;@GetMapping("/rules")@AuthAction(PrivilegeType.READ_RULE)public Result<List<AuthorityRuleEntity>> apiQueryAllRulesForMachine(@RequestParam String app,@RequestParam String ip,@RequestParam Integer port) {if (StringUtil.isEmpty(app)) {return Result.ofFail(-1, "app cannot be null or empty");}if (StringUtil.isEmpty(ip)) {return Result.ofFail(-1, "ip cannot be null or empty");}if (port == null || port <= 0) {return Result.ofFail(-1, "Invalid parameter: port");}if (!appManagement.isValidMachineOfApp(app, ip)) {return Result.ofFail(-1, "given ip does not belong to given app");}try {
//            List<AuthorityRuleEntity> rules = sentinelApiClient.fetchAuthorityRulesOfMachine(app, ip, port);List<AuthorityRuleEntity> rules = ruleProvider.getRules(app);rules = repository.saveAll(rules);return Result.ofSuccess(rules);} catch (Throwable throwable) {logger.error("Error when querying authority rules", throwable);return Result.ofFail(-1, throwable.getMessage());}}private <R> Result<R> checkEntityInternal(AuthorityRuleEntity entity) {if (entity == null) {return Result.ofFail(-1, "bad rule body");}if (StringUtil.isBlank(entity.getApp())) {return Result.ofFail(-1, "app can't be null or empty");}if (StringUtil.isBlank(entity.getIp())) {return Result.ofFail(-1, "ip can't be null or empty");}if (entity.getPort() == null || entity.getPort() <= 0) {return Result.ofFail(-1, "port can't be null");}if (entity.getRule() == null) {return Result.ofFail(-1, "rule can't be null");}if (StringUtil.isBlank(entity.getResource())) {return Result.ofFail(-1, "resource name cannot be null or empty");}if (StringUtil.isBlank(entity.getLimitApp())) {return Result.ofFail(-1, "limitApp should be valid");}if (entity.getStrategy() != RuleConstant.AUTHORITY_WHITE&& entity.getStrategy() != RuleConstant.AUTHORITY_BLACK) {return Result.ofFail(-1, "Unknown strategy (must be blacklist or whitelist)");}return null;}@PostMapping("/rule")@AuthAction(PrivilegeType.WRITE_RULE)public Result<AuthorityRuleEntity> apiAddAuthorityRule(@RequestBody AuthorityRuleEntity entity) {Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);if (checkResult != null) {return checkResult;}entity.setId(null);Date date = new Date();entity.setGmtCreate(date);entity.setGmtModified(date);try {entity = repository.save(entity);} catch (Throwable throwable) {logger.error("Failed to add authority rule", throwable);return Result.ofThrowable(-1, throwable);}
//        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
//            logger.info("Publish authority rules failed after rule add");
//        }try {publishRules(entity.getApp());} catch (Throwable throwable) {logger.error("Publish authority rules failed after rule add", throwable);return Result.ofThrowable(-1, throwable);}return Result.ofSuccess(entity);}@PutMapping("/rule/{id}")@AuthAction(PrivilegeType.WRITE_RULE)public Result<AuthorityRuleEntity> apiUpdateParamFlowRule(@PathVariable("id") Long id,@RequestBody AuthorityRuleEntity entity) {if (id == null || id <= 0) {return Result.ofFail(-1, "Invalid id");}Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);if (checkResult != null) {return checkResult;}entity.setId(id);Date date = new Date();entity.setGmtCreate(null);entity.setGmtModified(date);try {entity = repository.save(entity);if (entity == null) {return Result.ofFail(-1, "Failed to save authority rule");}} catch (Throwable throwable) {logger.error("Failed to save authority rule", throwable);return Result.ofThrowable(-1, throwable);}
//        if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
//            logger.info("Publish authority rules failed after rule update");
//        }try {publishRules(entity.getApp());} catch (Throwable throwable) {logger.error("Publish authority rules failed after rule update", throwable);return Result.ofThrowable(-1, throwable);}return Result.ofSuccess(entity);}@DeleteMapping("/rule/{id}")@AuthAction(PrivilegeType.DELETE_RULE)public Result<Long> apiDeleteRule(@PathVariable("id") Long id) {if (id == null) {return Result.ofFail(-1, "id cannot be null");}AuthorityRuleEntity oldEntity = repository.findById(id);if (oldEntity == null) {return Result.ofSuccess(null);}try {repository.delete(id);} catch (Exception e) {return Result.ofFail(-1, e.getMessage());}
//        if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
//            logger.error("Publish authority rules failed after rule delete");
//        }try {publishRules(oldEntity.getApp());} catch (Throwable throwable) {logger.error("Publish authority rules failed after rule delete", throwable);return Result.ofThrowable(-1, throwable);}return Result.ofSuccess(id);}//    private boolean publishRules(String app, String ip, Integer port) {
//        List<AuthorityRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
//        return sentinelApiClient.setAuthorityRuleOfMachine(app, ip, port, rules);
//    }private void publishRules(/*@NonNull*/ String app) throws Exception {List<AuthorityRuleEntity> rules = repository.findAllByApp(app);rulePublisher.publish(app, rules);}
}

15、我们以nacos-client-demo为例,配置一下nacos,模拟一下sentinel对这个项目的控制,贴出来json格式做参考。新建一个namespace叫做sentinel-rules,这样sentinel的配置单独放,然后创建5个dataId来对应这5个功能。

nacos-client-demo-flow-rules:

[{"resource": "/nacos-client-demo/api/talk","limitApp": "default","grade": 1,"count": 1,"clusterMode": false,"controlBehavior": 0,"strategy": 0,"warmUpPeriodSec": 10,"maxQueueingTimeMs": 500,"refResource": "rrr"}
]

nacos-client-demo-degrade-rules:

[{"app": "nacos-client-demo","resource": "/nacos-client-demo/api/talk","limitApp": "default","count": 0.5,"timeWindow": 10,"grade": 0,"minRequestAmount": 5,"statIntervalMs": 10000,"slowRatioThreshold": 0.5,"maxAllowedRt": 200}
]

nacos-client-demo-param-flow-rules:

[{"app": "nacos-client-demo","rule": {"burstCount": 0,"clusterConfig": {"fallbackToLocalWhenFail": true,"sampleCount": 10,"thresholdType": 0,"windowIntervalMs": 1000},"clusterMode": false,"controlBehavior": 0,"count": 3.0,"durationInSec": 1,"grade": 1,"limitApp": "default","maxQueueingTimeMs": 0,"paramFlowItemList": [],"paramIdx": 0,"regex": false,"resource": "/nacos-client-demo/api/talk"}}
]

nacos-client-demo-system-rules:

[{"app": "nacos-client-demo","avgRt": 1,"highestCpuUsage": -1.0,"highestSystemLoad": -1.0,"maxThread": -1,"qps": -1.0}
]

nacos-client-demo-authority-rules:

[{"app": "nacos-client-demo","rule": {"limitApp": "openfeign-demo","regex": false,"resource": "/nacos-client-demo/api/talk","strategy": 1}}
]

16、这就完成了,这时候用IDE运行一下测试一下,如果启动后,sentinel里面已经存在nacos-client-demo的这些配置,证明修改完成。

微服务使用带有nacos持久化的Sentinel

Sentinel Dashboard完成了接入nacos持久化,但是这还不够,我们还要将nacos-client-demo这个项目也实现接入带有nacos持久化的sentinel,这样才能实现三方的闭环,SpringCloud对这个支持的很好,只要配置一下即可。

1、修改pom文件,引入sentinel支持nacos持久化的依赖。

        <dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId></dependency>

2、application.yaml中添加sentinel对nacos持久化配置,就是配置nacos的地址以及上一节在nacos里添加的有关sentinel的几个dataId。

spring:application:name: nacos-client-demosentinel:transport:dashboard: 127.0.0.1:8080clientIp: 192.168.3.164http-method-specify: true # 请求前缀# 设置sentinel为热加载 默认为falseeager: true# 使用nacos来做持久化datasource:ds-flow:nacos:server-addr: 192.168.3.54:80data-id: ${spring.application.name}-flow-rulesusername: nacospassword: nacos# namespace必须用id,所以创建namespace的时候尽量填入idnamespace: sentinel-rulesgroup-id: devops## 配置存储的格式data-type: json## rule-type设置对应得规则类型,总共七大类型,在com.alibaba.cloud.sentinel.datasource.RuleType这个枚举类中有体现## flow:流控  degrade:熔断降级  param-flow:热点参数规则  system:系统保护规则  authority:授权规则rule-type: flow## 配置熔断降级规则,名字任意ds-degrade:nacos:server-addr: 192.168.3.54:80data-id: ${spring.application.name}-degrade-rulesusername: nacospassword: nacos# namespace必须用id,所以创建namespace的时候尽量填入idnamespace: sentinel-rulesgroup-id: devopsdata-type: jsonrule-type: degrade

3、重新运行项目即可,这时候的sentinel dashboard、nacos、nacos-client-demo三者两两之间数据互通了。

相关文章:

SpringCloud系列教程(十四):Sentinel持久化

Sentinel之前已经搭建和应用成功了&#xff0c;但是它有一个很大的缺点就是官方没有提供持久化的方案&#xff0c;从项目源码上看感觉这款工具也没有完成的太好&#xff0c;所以需要我们去对它进行二次开发。要补充的功能大概如下&#xff1a; 1、将Sentinel接入nacos中&#…...

Slider,InputField,Scroll View,Scrollbar及Layout组件

Slider组件 Fill Rect:填充滑动条选中区域的背景图部分 Handle Rect:滑动条的球 Direction:滑动条的滑动方向 Min Value:起始位置的数值&#xff08;浮点数&#xff09; Max Value:结束位置的数值&#xff08;浮点数&#xff09; Whole Numbers:必须为整数&#xff08;布尔…...

ollama注册自定义模型(GGUF格式)

文章目录 ollama注册自定义模型&#xff08;GGUF格式&#xff09;下载模型注册模型(GGUF格式) ollama注册自定义模型&#xff08;GGUF格式&#xff09; 需要全程开启ollama nohup ollama serve > ollama.log 2>&1 &需要注意&#xff0c;尽管手动下载的GGUF格式模…...

【算法】 区间合并(附蓝桥杯真题) python

步骤 1.先将所有区间按左端点排序 2.从前往后扫一遍&#xff0c;维护当前正在合并的区间[st, ed] 3.依次检查每个区间[l, r]&#xff0c; 若 l > ed,将[st, ed]加入 ans , 更新st l,ed r 若 l < ed &#xff0c;更新ed max(ed, r) 时间复杂度 O(nlogn) 模板 https:/…...

关于重构分析查询界面的思考(未完)

业务系统里&#xff0c;查询界面很常见&#xff0c;数据分析场景需求普遍而迫切&#xff0c;而新的技术也在不断出现&#xff0c;很有必要重构分析查询界面。 查询筛选 为了尽可能从数据中发现&#xff0c;需要尽可能地将查询条件添加进来&#xff0c;可这样&#xff0c;查询…...

机器人技能列表

一、机器人制作基础入门 &#xff08;一&#xff09;机器人概述 1.机器人的定义与分类 2.机器人的发展历程与现状 3.机器人在各领域的应用案例 &#xff08;二&#xff09;必备工具与材料 4.常用电子工具介绍&#xff08;万用表、电烙铁等&#xff09; 5.机械加工工具&…...

五大基础算法——分治算法

分治算法 是一种通过将问题分解为多个规模较小的子问题&#xff0c;递归解决子问题&#xff0c;然后将子问题的解合并为原问题解的算法思想。它通常包含三个步骤&#xff1a;分解&#xff08;Divide&#xff09;、解决&#xff08;Conquer&#xff09; 和 合并&#xff08;Comb…...

HarmonyOS NEXT 声明式UI语法学习笔记-创建自定义组件

基础语法概述 ArkTS的基本组成 装饰器&#xff1a;用于装饰类、结构、方法以及变量&#xff0c;并赋予其特殊含义。如上图都是装饰器&#xff0c;Component表示自定义组件&#xff0c;Entry表示表示自定义组件的入口组件&#xff0c;State表示组件中的状态变量&#xff0c;当状…...

补充二分LIS

B3637 最长上升子序列 题目描述 这是一个简单的动规板子题。 给出一个由 n ( n ≤ 5000 ) n(n\le 5000) n(n≤5000) 个不超过 1 0 6 10^6 106 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。 最长上升子序列是指&#xff0c;从原序列中按顺序取出一些数字排…...

用户模块——握手验证

1. 引言 在现代 Web 应用中&#xff0c;WebSocket 以其全双工通信、低延迟、减少 HTTP 开销等优势&#xff0c;被广泛应用于即时通讯、在线游戏、实时数据推送等场景。然而&#xff0c;在涉及用户认证时&#xff0c;WebSocket 存在一个常见问题——每次刷新页面都会重新建立 We…...

97.HarmonyOS NEXT跑马灯组件教程:基础概念与架构设计

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; HarmonyOS NEXT跑马灯组件教程&#xff1a;基础概念与架构设计 1. 跑马灯组件概述 跑马灯&#xff08;Marquee&#xff09;是一种常见的UI组件&a…...

81.HarmonyOS NEXT 状态管理与响应式编程:@Observed深度解析

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; HarmonyOS NEXT 状态管理与响应式编程&#xff1a;Observed深度解析 文章目录 HarmonyOS NEXT 状态管理与响应式编程&#xff1a;Observed深度解析…...

【Agent】OpenManus-Agent架构详细分析

各组件详细设计见&#xff1a; BaseAgent&#xff1a;BaseAgentReActAgent&#xff1a;ReActAgentToolCallAgent&#xff1a;ToolCallAgent具体Agent实现&#xff1a;具体AgentMemory数据结构&#xff1a;Memory 1. 智能体层次结构 OpenManus 采用了一个多层次的智能体继承结…...

股指期货有卖不出去的时候吗?

在股指期货的交易世界里&#xff0c;很多人都有这样的疑问&#xff1a;股指期货会不会有卖不出去的时候呢&#xff1f;答案是会的&#xff0c;下面咱们就来详细唠唠为啥会出现这种情况。 市场极端行情下难以卖出 1.跌停限制&#xff1a;股指期货和股票一样&#xff0c;也有涨…...

前端Html5 Canvas面试题及参考答案

目录 Canvas 元素的默认尺寸是多少?如何正确设置其宽高以避免图像拉伸? 如何获取 Canvas 的 2D 上下文对象?3D 上下文支持哪些技术? canvas.width 与 canvas.style.width 的区别是什么? Canvas 支持的图像格式有哪些?如何将 Canvas 转换为 Base64 图片? Canvas 中如…...

【ES6】03-Set + Map

本文介绍两种集合 set map 的操作和方法。 目录 1. Set 1.1 set基本使用 1.2 add 1.3 delete 1.4 has 1.5 size 1.6 set转换为数组 1.7 拓展运算符 1.8 for...of 1.9 forEach 1.10 set给数组去重 2. Map 2.1 创建map集合 2.2 set添加元素 2.3 delete删除元素 …...

Java缓存String(字符串常量池)、Integer (-128 到 127 )

对问题的解释 1. “字符串常量池存储的是string对象的直接引用&#xff0c;而不是直接存放的对象&#xff0c;是一张string table” 的含义 这句话可以从以下几个方面理解&#xff1a; (1) 字符串常量池的存储内容 直接引用&#xff1a;字符串常量池中存储的是指向实际 Stri…...

消息队列的特性与使用场景:Kafka、ActiveMQ、RabbitMQ与RocketMQ的深度剖析

在分布式系统和微服务架构中&#xff0c;消息队列是实现服务间通信和解耦的核心组件。Kafka、ActiveMQ、RabbitMQ和RocketMQ是当前最受欢迎的消息队列解决方案&#xff0c;它们各自具有独特的特性和适用场景。本文将从特性和使用场景两个维度进行对比分析&#xff0c;帮助读者更…...

开发、科研、日常办公工具汇总(自用,持续更新)

主要记录汇总一下自己平常会用到的网站工具&#xff0c;方便查阅。 update&#xff1a;2025/2/11&#xff08;开发网站补一下&#xff09; update&#xff1a;2025/2/21&#xff08;补充一些AI工具&#xff0c;刚好在做AI视频相关工作&#xff09; update&#xff1a;2025/3/7&…...

解决VueI18n使用浏览器插件翻译后,切换国际化失效的问题

问题复现 在使用Vue-i18n对页面进行国际化的时候&#xff0c;使用浏览器翻译插件&#xff08;如腾讯翻译&#xff09;后&#xff0c;切换国际化语言&#xff0c;随后当我们关闭浏览器翻译插件后&#xff0c;再次切换国际化语言&#xff0c;原来被翻译的文字无法正确切换 出现…...

HTML5 drag API实现列表拖拽排序

拖拽API&#xff08;Drag and Drop API&#xff09;是HTML5提供的一组功能&#xff0c;使得在网页上实现拖放操作变得更加简单和强大。这个API允许开发者为网页元素添加拖拽功能&#xff0c;用户可以通过鼠标将元素拖动并放置到指定的目标区域。 事件类型 dragstart&#xff1…...

改变一生的思维模型【11】升维

升维思维模型&#xff1a;突破认知局限的破局法则 一、定义与核心逻辑 升维思维是通过增加分析维度&#xff0c;将问题投射到更高认知层次寻找解决方案的思考方式。其本质是跳出原有竞争维度&#xff0c;在更广阔的空间重构游戏规则。核心逻辑在于&#xff1a;当低维战场陷入…...

【动手学深度学习】#2线性神经网络

主要参考学习资料&#xff1a; 《动手学深度学习》阿斯顿张 等 著 【动手学深度学习 PyTorch版】哔哩哔哩跟李牧学AI 目录 2.1 线性回归2.1.1 线性回归的基本元素线性模型损失函数解析解随机梯度下降 2.1.3 最大似然估计 2.2 线性回归从零开始实现2.2.1 生成数据集2.2.2 读取数…...

计算机网络——NAT

一、什么是NAT&#xff1f; NAT&#xff08;Network Address Translation&#xff0c;网络地址转换&#xff09; 是一种将 私有IP地址 与 公有IP地址 相互映射的技术&#xff0c;主要用于解决IPv4地址不足的问题。它像一名“翻译官”&#xff0c;在数据包经过路由器或防火墙时…...

Stable Deffusion--常见模型插件详解

1.Checkpoint大模型 Checkpoint 是生成图像的基础模型&#xff0c;决定了整体画风如动漫、写实、机甲等。它是必选项&#xff0c;所有图像生成必须基于一个主模型。文件体积较大通常 1.5GB 以上&#xff0c;格式为 .ckpt 或 .safetensors。 存放位置为&#xff1a;\models\Sta…...

防重复提交详解:从前端Vue到后端Java的全面解决方案

防重复提交详解&#xff1a;从前端Vue到后端Java的全面解决方案 一、重复提交问题概述 在Web应用开发中&#xff0c;表单重复提交是一个常见问题&#xff0c;可能导致&#xff1a; 数据库中出现重复记录重复执行业务逻辑&#xff08;如多次扣款&#xff09;系统资源浪费用户…...

同一子网通信

添加交换机后的通信流程 1. 同一子网内&#xff08;使用交换机&#xff09; 判断是否在同一子网&#xff1a; 主机A通过子网掩码判断主机B的IP地址是否属于同一子网。若在同一子网&#xff0c;主机A需要通过ARP获取主机B的MAC地址。 ARP请求&#xff08;广播&#xff09;&…...

快速迭代:利用 nodemon 和其他工具实现 Express.js 热更新

在开发 Express.js 应用时&#xff0c;热更新&#xff08;Hot Reloading&#xff09;可以显著提升开发效率&#xff0c;因为它允许你在修改代码后立即看到效果&#xff0c;而无需手动重启服务器。以下是几种实现热更新的方法和工具&#xff0c;帮助你在开发过程中更高效地工作。…...

BUG日志:Maven项目启动报错(文件名或者文件扩展名过长)

Bug日志编号&#xff1a;[Maven-001] 标题&#xff1a;Windows系统下Maven项目因路径过长导致命令行执行失败 1. 问题描述 现象&#xff1a;执行mvn clean install时报错&#xff1a; The input line is too long 或 The filename or extension is too long触发条件&#xf…...

IntelliJ IDEA 快捷键系列:重命名快捷键详解

目录 引言一、默认重命名快捷键1. Windows 系统‌2. Mac 系统‌ 二、操作步骤与技巧1. 精准选择重命名范围‌2. 智能过滤无关内容‌ 三、总结 引言 在代码重构中&#xff0c;‌重命名变量、类、方法‌ 是最常用的操作之一。正确使用快捷键可以极大提升开发效率。本文针对 ‌Ma…...