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.pswp.cn/diannao/73502.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/73502.shtml
英文地址,請注明出處:http://en.pswp.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;子任務…