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三者两两之间数据互通了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/73502.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AGI大模型(3):大模型生成内容

1 大模型是怎么生成内容的 简单来说就是靠"猜"! 虽然⾮常不可思议,但事实就是这样,现阶段所有的 NLP 任务,都不意味着机器真正理解这个世界,它只是在玩⽂字游戏,进⾏⼀次⼜⼀次的概率解谜,本质上和我们玩报纸上的填字游戏是⼀个逻辑。只是我们靠知识和智慧,…

Go语言环境搭建并执行第一个Go程序

目录 一、Windows环境搭建 二、vscode安装插件 三、运行第一个go程序 一、Windows环境搭建 下载Go&#xff1a;All releases - The Go Programming Language 这里是Windows搭建&#xff0c;选择的是windows-amd64.msi&#xff0c;也可以选择zip直接解压缩到指定目录 选择msi…

Java数据结构第二十三期:Map与Set的高效应用之道(二)

专栏&#xff1a;Java数据结构秘籍 个人主页&#xff1a;手握风云 目录 一、哈希表 1.1. 概念 1.2. 冲突 1.3. 避免冲突 1.4. 解决冲突 1.5. 实现 二、OJ练习 2.1. 只出现一次的数字 2.2. 随机链表的复制 2.3. 宝石与石头 一、哈希表 1.1. 概念 顺序结构以及平衡树中…

OpenHarmony子系统开发 - Rust编译构建指导

OpenHarmony子系统开发 - Rust编译构建指导 一、Rust模块配置规则和指导 概述 Rust是一门静态强类型语言&#xff0c;具有更安全的内存管理、更好的运行性能、原生支持多线程开发等优势。Rust官方也使用Cargo工具来专门为Rust代码创建工程和构建编译。 OpenHarmony为了集成C…

【SpringMVC】常用注解:@ModelAttribute

1.作用 该注解是在SpringMVC4.3版本后新加入的。它可以修饰方法和参数。出现在方法上&#xff0c;表示当前方法会在控制器的方法之前执行。它可以修饰 没有返回值的方法&#xff0c;也可以修饰没有返回值的方法。它修饰参数&#xff0c;获取指定 的数据给参数赋值。 当表单提…

人工智能之数学基础:如何将线性变换转换为矩阵?

本文重点 在机器学习中,常用的理论就是线性变换,线性变化一定有对应的矩阵表示,非线性变换是不具备这个性质的,那么现在如果有一个线性变换T那么如何知道它对应的矩阵呢? 线性变换的本质 我们知道线性变换相当于一个函数,而矩阵也是一个函数,所以线性变换一定存在一个…

STM32驱动代码规范化编写指南(嵌入式C语言方向)

点击下面图片&#xff0c;为您提供全新的嵌入式学习路线 文章目录 一、命名规范体系1.1 变量/函数命名1.2 宏定义规范1.3 类型定义 二、代码结构组织2.1 文件组织结构2.2 头文件规范模板 三、注释体系构建3.1 Doxygen风格示例3.2 复杂逻辑注释 四、硬件抽象层设计4.1 寄存器封…

C++Primer学习(7.1 定义抽象数据类型)

类的基本思想是数据抽象(data abstraction)和封装(encapsulation)。数据抽象是种依赖于接口(interface)和实现(implementation)分离的编程(以及设计)技术。类的接口包括用户所能执行的操作:类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。 封…

【人工智能】大语言模型学习大纲

大语言模型学习大纲 大语言模型学习知识点大纲一、基础知识准备二、机器学习入门三、自然语言处理(NLP)基础四、Transformer架构与实践五、高级主题六、前沿研究与实战项目 学习步骤第一步&#xff1a;打牢基础第二步&#xff1a;掌握机器学习与深度学习基础第三步&#xff1a;…

Trae与Builder模式初体验

说明 下载的国际版&#xff1a;https://www.trae.ai/ 建议 要选新模型 效果 还是挺不错的&#xff0c;遇到问题反馈一下&#xff0c;AI就帮忙解决了&#xff0c;真是动动嘴&#xff08;打打字就行了&#xff09;&#xff0c;做些小的原型效果或演示Demo很方便呀&#xff…

基于VM的CentOS 7.4系统安装与配置说明系统环境主机系统

系统环境 主机系统&#xff1a;Windows 11虚拟机版本&#xff1a;VMware Workstation 17 ProDVD镜像版本&#xff1a;CentOS-7-x86_64-DVD-1908 虚拟机配置 内存&#xff1a;1G处理器&#xff1a;1核硬盘&#xff1a;80G 安装步骤 1. 准备镜像文件 下载并获取CentOS 7.4的…

【设计模式】《设计模式:可复用面向对象软件的基础》:设计模式怎样解决设计问题?

文章目录 ⭐前言⭐一、设计模式怎样解决设计问题&#xff1f;&#x1f31f;1、寻找合适的对象&#x1f31f;2、决定对象的粒度&#x1f31f;3、指定对象接口&#x1f31f;4、描述对象的实现&#x1f31f;5、运用复用机制✨(1)针对接口编程&#xff0c;而不是针对实现编程。✨(2…

【SpringMVC】常用注解:@MatrixVariable

1.作用 接收矩阵变量传送的值 或许有人听都没听过矩阵变量是什么&#xff0c;下面来介绍一下 矩阵变量是一种在URL路径中传递多个键值对参数的方式&#xff0c;它是在 Servlet 规范之外的一种扩展机制&#xff0c;可用于更灵活地传递参数。 例如&#xff1a;/cars;colorred…

【项目管理git】git学习

ps&#xff1a;所有东西都是个人理解 文章目录 一、git是什么&#xff0c;它用来做什么&#xff1f;二、相关知识库2.1 简单的linux指令2.2 git配置指令2.3 git常见的指令2.3.1 Git的上传原理2.3.2 版本回退相关内容 2.4 设置远程地址&#xff0c;本地上传到github2.4.1 ssh相…

【性能优化】MySQL 生产环境 SQL 性能优化实战案例

&#x1f680; MySQL 生产环境 SQL 性能优化实战案例 &#x1f3d7;️ 背景介绍 最近在处理一个项目时&#xff0c;发现在生产环境的工作流相关接口中&#xff0c;某些查询的执行时间异常缓慢&#xff0c;尽管数据量仅为 2 万条。经过分析&#xff0c;发现以下 SQL 语句执行非…

python速通小笔记-------1.容器

1.字符串的标识 字符串需要用“”标识。 与c不同&#xff0c;python 写变量时 不需要标明数据类型每一行最后不需要加&#xff1b; 2.print函数的使用 与c中的printf函数一致 3.运算符 4.字符串str操作 1. 实现字符串拼接 2.% 实现字符串初始化 %s占位会把变量强制转变为…

【SpringMVC】常用注解:@SessionAttributes

1.作用 用于多次执行控制器方法间的参数共享 2.属性 value&#xff1a;用于指定存入的属性名称 type&#xff1a;用于指定存入的数据类型 3.示例 先写JSP代码 <a href"demo1/putMethod">存入 SessionAttribute</a><br><a href"demo…

零基础上手Python数据分析 (2):Python核心语法快速入门

写在前面 场景:每周销售数据报表整理 任务描述: 你需要每周从多个Excel文件中汇总销售数据,计算各项指标(销售额、订单量、客单价等),并生成周报。Excel操作痛点: 文件太多,手动打开复制粘贴,效率低下,容易出错。 多个Excel文件,每个都要打开、筛选、复制数据,重复…

【PHP】获取PHP-FPM的状态信息

文章目录 一、前言二、环境三、过程1&#xff09;修改PHP-FPM配置文件2&#xff09;修改Nginx配置文件3&#xff09;访问页面4&#xff09;修改状态页面端口 一、前言 PHP-FPM内置有一个状态页面&#xff0c;通过这个页面可以获取到FPM的一些状态信息&#xff08;见下图&#…

CCF CSP 第30次(2023.09)(2_坐标变换(其二)_C++)

CCF CSP 第30次&#xff08;2023.09&#xff09;&#xff08;2_坐标变换&#xff08;其二&#xff09;_C&#xff09; 题目背景&#xff1a;题目描述&#xff1a;输入格式&#xff1a;输出格式&#xff1a;样例输入&#xff1a;样例输出&#xff1a;样例解释&#xff1a;子任务…