- 921. Minimum Add to Make Parentheses Valid 使括号有效的最少添加
- 915. Partition Array into Disjoint Intervals 分割数组
- 932. Beautiful Array 漂亮数组
- 940. Distinct Subsequences II 不同的子序列 II
规则持久化分成两种方式:拉模式和推模式。
加依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
增加拉模式规则持久化类
/**
* 拉模式规则持久化
*
* @author itmuch.com
*/
public class FileDataSourceInit implements InitFunc {
@Override
public void init() throws Exception {
// TIPS: 如果你对这个路径不喜欢,可修改为你喜欢的路径
String ruleDir = System.getProperty("user.home") + "/sentinel/rules";
String flowRulePath = ruleDir + "/flow-rule.json";
String degradeRulePath = ruleDir + "/degrade-rule.json";
String systemRulePath = ruleDir + "/system-rule.json";
String authorityRulePath = ruleDir + "/authority-rule.json";
String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
this.mkdirIfNotExits(ruleDir);
this.createFileIfNotExits(flowRulePath);
this.createFileIfNotExits(degradeRulePath);
this.createFileIfNotExits(systemRulePath);
this.createFileIfNotExits(authorityRulePath);
this.createFileIfNotExits(paramFlowRulePath);
// 流控规则
ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
flowRulePath,
flowRuleListParser
);
// 将可读数据源注册至FlowRuleManager
// 这样当规则文件发生变化时,就会更新规则到内存
FlowRuleManager.register2Property(flowRuleRDS.getProperty());
WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
flowRulePath,
this::encodeJson
);
// 将可写数据源注册至transport模块的WritableDataSourceRegistry中
// 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
// 降级规则
ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
degradeRulePath,
degradeRuleListParser
);
DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
degradeRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
// 系统规则
ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
systemRulePath,
systemRuleListParser
);
SystemRuleManager.register2Property(systemRuleRDS.getProperty());
WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
systemRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
// 授权规则
ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
authorityRulePath,
authorityRuleListParser
);
AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
authorityRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
// 热点参数规则
ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
paramFlowRulePath,
paramFlowRuleListParser
);
ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
paramFlowRulePath,
this::encodeJson
);
ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
}
private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<FlowRule>>() {
}
);
private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<DegradeRule>>() {
}
);
private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<SystemRule>>() {
}
);
private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<AuthorityRule>>() {
}
);
private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<ParamFlowRule>>() {
}
);
private void mkdirIfNotExits(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
}
private void createFileIfNotExits(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.createNewFile();
}
}
private <T> String encodeJson(T t) {
return JSON.toJSONString(t);
}
}
配置
在项目的 resources/META-INF/services 目录下创建文件,名为 com.alibaba.csp.sentinel.init.InitFunc ,内容为:
# 改成上面FileDataSourceInit的包名类名全路径即可。
com.itmuch.contentcenter.FileDataSourceInit
优缺点分析
优点
缺点
FileRefreshableDataSource
定时更新的,所以规则更新会有延迟。如果FileRefreshableDataSource
定时时间过大,可能长时间延迟;如果FileRefreshableDataSource
过小,又会影响性能;控制台推送规则:
将规则推送到Nacos或其他远程配置中心
Sentinel客户端链接Nacos,获取规则配置;并监听Nacos配置变化,如发生变化,就更新本地缓存(从而让本地缓存总是和Nacos一致)
控制台监听Nacos配置变化,如发生变化就更新本地缓存(从而让控制台本地缓存总是和Nacos一致)
控制面板改造
首先我们来改造sentinel的控制面板。在源码中官方已经给出来了单元测试。我们来进行改造。
我们在java包下面的com.alibaba.csp.sentinel.dashboard.rule
创建一个apollo包,开始创建我们自己的类。
ApolloConfig
由于我们要将限流配置保存到Apollo中,所以我们需要配置地址和调用OpenAPI的token。
@Configuration
@EnableApolloConfig
public class ApolloConfig {
/**
* apollo地址
*/
@Value("${apollo.sentinel.portal.url}")
private String apolloPortalUrl;
/**
* apollo token
*/
@Value("${apollo.sentinel.token}")
private String apolloApplicationToken;
/**
* @Author www.ddkk.com
* @Description apollo openApi
* @Date 09:39 2020-06-02
* @param
* @return
**/
@Bean
public ApolloOpenApiClient apolloOpenApiClient() {
ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder()
.withPortalUrl(apolloPortalUrl)
.withToken(apolloApplicationToken)
.build();
return client;
}
}
ApolloCommonService
主要用来对Apollo里的值和Sentinel中的实体类相互转换。在保存规则和读取规则都会调这里面的方法。
@Service
public class ApolloCommonService {
/**
* 没有找到配置项,apollo 返回的错误码
*/
private static final int NO_FOUND_ERROR_CODE = 404;
@Autowired
private ApolloOpenApiClient apolloOpenApiClient;
@Value("${apollo.sentinel.env}")
private String env;
@Value("${apollo.sentinel.appid}")
private String appId;
@Value("${apollo.sentinel.cluster.name}")
private String clusterName;
@Value("${apollo.sentinel.namespace.name}")
private String namespaceName;
@Value("${apollo.sentinel.modify.user}")
private String modifyUser;
@Value("${apollo.sentinel.modify.comment}")
private String modifyComment;
@Value("${apollo.sentinel.release.comment}")
private String releaseComment;
@Value("${apollo.sentinel.release.user}")
private String releaseUser;
/**
* @Author www.ddkk.com
* @Description 从apollo中获取规则
* @Date 19:52 2020-06-02
* @param
* @return
**/
public <T> List<T> getRules(String appName, String flowDataIdSuffix, Class<T> ruleClass) {
// flowDataId
String flowDataId = appName + flowDataIdSuffix;
OpenNamespaceDTO openNamespaceDTO = apolloOpenApiClient.getNamespace(appId, env, clusterName, namespaceName);
String rules = openNamespaceDTO
.getItems()
.stream()
.filter(p -> p.getKey().equals(flowDataId))
.map(OpenItemDTO::getValue)
.findFirst()
.orElse("");
if (StringUtil.isEmpty(rules)) {
return new ArrayList<>();
}
List<T> flow = JSON.parseArray(rules, ruleClass);
if (Objects.isNull(flow)) {
return new ArrayList<>();
}
return flow;
}
/**
* @Author www.ddkk.com
* @Description 设置规则类型
* @Date 01:31 2020-06-03
* @param
* @return
**/
public void publishRules(String appName, String flowDataIdSuffix, String rules) {
// flowDataId
String flowDataId = appName + flowDataIdSuffix;
AssertUtil.notEmpty(appName, "app name cannot be empty");
if (rules == null) {
return;
}
OpenItemDTO openItemDTO = new OpenItemDTO();
openItemDTO.setKey(flowDataId);
openItemDTO.setValue(rules);
openItemDTO.setComment(modifyComment);
openItemDTO.setDataChangeCreatedBy(modifyUser);
try {
apolloOpenApiClient.createOrUpdateItem(appId, env, clusterName, namespaceName, openItemDTO);
} catch (Exception e) {
if (e.getCause() instanceof ApolloOpenApiException) {
ApolloOpenApiException apolloOpenApiException = (ApolloOpenApiException) e.getCause();
if (Integer.valueOf(NO_FOUND_ERROR_CODE).equals(apolloOpenApiException.getStatus())) {
apolloOpenApiClient.createItem(appId, env, clusterName, namespaceName, openItemDTO);
System.out.println("初始化应用配置 -> {}" + flowDataId);
}
} else {
e.printStackTrace();
}
}
// Release configuration
NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();
namespaceReleaseDTO.setEmergencyPublish(true);
namespaceReleaseDTO.setReleaseComment(releaseComment);
namespaceReleaseDTO.setReleasedBy(releaseUser);
namespaceReleaseDTO.setReleaseTitle(releaseComment);
apolloOpenApiClient.publishNamespace(appId, env, clusterName, namespaceName, namespaceReleaseDTO);
}
/**
* @Author www.ddkk.com
* @Description 删除规则
* @Date 01:33 2020-06-03
* @param
* @return
**/
public void deleteRules(String rulekey, String operator) {
try {
apolloOpenApiClient.removeItem(appId, env, clusterName, namespaceName, rulekey, operator);
} catch (Exception e) {
if (e.getCause() instanceof ApolloOpenApiException) {
ApolloOpenApiException apolloOpenApiException = (ApolloOpenApiException) e.getCause();
if (Integer.valueOf(NO_FOUND_ERROR_CODE).equals(apolloOpenApiException.getStatus())) {
apolloOpenApiClient.removeItem(appId, env, clusterName, namespaceName, rulekey, operator);
}
} else {
e.printStackTrace();
}
}
// Release configuration
NamespaceReleaseDTO namespaceReleaseDTO = new NamespaceReleaseDTO();
namespaceReleaseDTO.setEmergencyPublish(true);
namespaceReleaseDTO.setReleaseComment(releaseComment);
namespaceReleaseDTO.setReleasedBy(releaseUser);
namespaceReleaseDTO.setReleaseTitle(releaseComment);
apolloOpenApiClient.publishNamespace(appId, env, clusterName, namespaceName, namespaceReleaseDTO);
}
}
AbstractApolloCommonService
基础抽象类,主要定义类Apollo中配置的不同规则前缀和上面提到的Apollo规则的处理类。
@Service
public abstract class AbstractApolloCommonService {
@Autowired
protected ApolloCommonService apolloCommonService;
/**
* 流控规则前缀标示
*/
@Value("${apollo.sentinel.flow.key.suffix:-flow}")
String flowDataIdSuffix;
/**
* 熔断降级规则前缀标示
*/
@Value("${apollo.sentinel.degrade.key.suffix:-degrade}")
String degradeDataIdSuffix;
/**
* 热点规则前缀标示
*/
@Value("${apollo.sentinel.paramFlow.key.suffix:-paramFlow}")
String paramFlowDataIdSuffix;
/**
* 系统规则前缀标示
*/
@Value("${apollo.sentinel.system.key.suffix:-system}")
String systemDataIdSuffix;
/**
* 授权规则前缀标示
*/
@Value("${apollo.sentinel.authority.key.suffix:-authority}")
String authorityDataIdSuffix;
}
DynamicRuleProvider和DynamicRulePublisher
public interface DynamicRuleProvider<T> {
T getRules(String appName) throws Exception;
}
public interface DynamicRulePublisher<T> {
void publish(String app, T rules) throws Exception;
}
这个是Sentinel提供的对规则的处理接口。分别包含getRules
和publish
方法用来对规则进行获取和保存,针对不同的限流规则我们实现不同的实体类就可以了。我们用限流规则来举例。
FlowRuleApolloProvider和FlowRuleApolloPublisher
@Component("flowRuleApolloProvider")
public class FlowRuleApolloProvider extends AbstractApolloCommonService implements
DynamicRuleProvider<List<FlowRuleEntity>> {
@Override
public List<FlowRuleEntity> getRules(String appName) {
return apolloCommonService.getRules(appName, flowDataIdSuffix, FlowRuleEntity.class);
}
}
@Component("flowRuleApolloPublisher")
public class FlowRuleApolloPublisher extends AbstractApolloCommonService implements
DynamicRulePublisher<List<FlowRuleEntity>> {
@Override
public void publish(String app, List<FlowRuleEntity> rules) {
apolloCommonService.publishRules(app, flowDataIdSuffix, JSON.toJSONString(rules));
}
}
FlowControllerV2
这是控制台的限流处理类,需要引用我们刚刚创建的类。
@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {
...
@Autowired
@Qualifier("flowRuleApolloProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleApolloPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
...
}
sidebar.html
修改页面,让他调用V2的controller
<li ui-sref-active="active" ng-if="!entry.isGateway">
<a ui-sref="dashboard.flow({app: entry.app})">
<i class="glyphicon glyphicon-filter"></i> 流控规则</a>
</li>
重启下sentinel控制台,设置一下流控规则,我们就看到规则已经保存到Apollo中了。
客户端改造
引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-apollo</artifactId>
</dependency>
增加Apollo配置
apollo:
meta: http://39.106.161.250:8080
bootstrap:
enabled: true
namespaces: FISH.Sentinel-Common
增加限流规则配置,告诉客户端如何读取Apollo中的配置规则。
spring:
cloud:
sentinel:
filter:
enabled: true
transport:
dashboard: localhost:8080
datasource:
ds:
apollo:
namespaceName: FISH.Sentinel-Common
flowRulesKey: order-flow
ruleType: flow
这样客户端就能动态读取Apollo配置的规则了。
这也不是一篇非常详细的源码领读,源码细节还需你自己仔细咀嚼 这只是我在改了些sentinel bug后,梳理的脉络,主要是脉络。看完后对Sentinel的源码模块划分和大致交互有个整体印象。 从而
还有其他类似的问题,但我想将其归结为最基本的问题。 我正在运行一个 .NET 应用程序 (C#) 并尝试连接并监视一组运行哨兵 ( 3x 哨兵监控 1 个主站和 2 个从站)。它们在 linux 机器
我们有 2 个运行 HA 应用程序的应用程序/Web 服务器,我们需要设置具有高可用性/复制功能的 Redis 以支持我们的应用程序。 考虑到 3 个节点的最低哨兵设置要求。 我们计划准备第一个应用程
我想知道是否可以将 Azure AD 用户配置文件详细信息(使用位置、国家或地区、办公室)获取到 azure Sentinel 日志中? Kusto 查询会是什么? 最佳答案 此功能将作为 Azure
Sentinel Dashboard限流规则保存 sentinel在限流规则配置方面提供了可视化页面 sentinel dashboard,源码可从github下载,请自行搜索,此处不提供下载链接
概念 何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如: 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
概述 除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积。Sentinel 熔断降级会在调
前言 在上一节中我们知道Sentinel 支持以下几种规则:流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则 和 热点参数规则。 Sentinel 的所有规则都可以在内存态中动态地查询及修改
在之前使用Nacos持久化规则的文档中,最后发现只能使用Nacos推送配置到控制台,那么怎么实现控制台和Nacos的双向同步呢? 这里不直接提供解决方案,我们还是先分析下控制台的源码。 下面我们分
化整为零 我们已经知道了Slot是从第一个往后一直传递到最后一个的,且当信息传递到StatisticSlot时,这里就开始进行统计了,统计的结果又会被后续的Slot所采用,作为规则校验的依据。我们先
Sentinel简介 Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定
之前遗留问题 之前我们集成了Nacos,实现了推送规则到客户端,但是控制台添加的规则不能持久化到Naocs中,这时需要对控制台源码进行改造。 先来回顾下默认添加流控规则的执行流程: 1、 控
Sentinel 中有很多比较重要的概念,我们要了解一个框架,首先要对框架中重要的概念实体进行分析,本文我将跟大家一起来分析一下 Sentinel 中非常重要的几个概念。 Resource Res
通过sentinel 的控制台,我们可以对规则进行查询和修改,也可以查看到实时监控,机器列表等信息,所以我们需要对 sentinel 的控制台做个完整的了解。 启动控制台 从github上下载源码
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定
控制台 控制台主要的处理类是 FlowControllerV1 。 @RestController @RequestMapping(value = "/v1/flow") pu
项目结构 将Sentinel的源码fork到自己的github库中,接着把源码clone到本地,然后开始源码阅读之旅吧。 首先我们看一下Sentinel项目的整个结构: sentine
雪崩效应 雪崩 一种自然现象,当山坡积雪内部的内聚力抗拒不了它所受到的重力拉引时,便向下滑动,引起大量雪体崩塌。 雪崩效应广泛应用于各种领域,比如密码学术语、管理学、商业等。 服务雪崩 在软件
实时监控 集成控制台后,当有请求时,实时监控页面会显示当前服务各个接口的访问信息,以图表的形式展示给用户,包含访问时间、通过 QPS、拒绝QPS、响应时间(ms)等信息。 簇点链路 列表:用
实时监控 Sentinel 提供对所有资源的实时监控,我们可以通过控制台查看。 可以很清楚的看到当前QPS流量趋势。 监控API 簇点监控 获取簇点列表 API: GET /cluste
我是一名优秀的程序员,十分优秀!