- sentinal提供了很多场景的demo程序,这里看下
sentinel-demo-basic
里面的几个demo - 配置可以nacos,apollo配置中心下发的方式获取,而demo中都是启动时初始化配置保存在内存中。
-
规则类图
一 黑白名单
- 根据调用来源限制白名单或黑名单
1.1 规则AuthorityRule
-
private int strategy
配置类型:0 白名单,1 黑名单 -
String resource
规则目标资源名称 -
String limitApp
规则应用的调用来源,逗号分隔多个。
1.2 初始化配置规则
private static void initWhiteRules() {
AuthorityRule rule = new AuthorityRule();
rule.setResource(RESOURCE_NAME);
rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
rule.setLimitApp("appA,appE");
AuthorityRuleManager.loadRules(Collections.singletonList(rule));
}
1.3 配置规则管理AuthorityRuleManager
1.3.1 初始化
- 初始化规则存储结构
private static Map<String, Set<AuthorityRule>> authorityRules = new ConcurrentHashMap<>();
- 初始化配置规则变更监听回调
private static final RulePropertyListener LISTENER = new RulePropertyListener();
- 配置规则接口,规则变更时调用currentProperty.updateValue(),触发注册的listener回调,更新相应的缓存规则
private static SentinelProperty<List<AuthorityRule>> currentProperty = new DynamicSentinelProperty<>();
- 初始化SentinelProperty的listener
static {
currentProperty.addListener(LISTENER);
}
1.3.2 RulePropertyListener
- 变更回调接口,根据数据源获取的变更的配置规则,更新缓存的配置规则
public void configUpdate(List<AuthorityRule> conf) {
Map<String, Set<AuthorityRule>> rules = loadAuthorityConf(conf);
authorityRules.clear();
if (rules != null) {
authorityRules.putAll(rules);
}
RecordLog.info("[AuthorityRuleManager] Authority rules received: " + authorityRules);
}
public void configLoad(List<AuthorityRule> value) {
Map<String, Set<AuthorityRule>> rules = loadAuthorityConf(value);
authorityRules.clear();
if (rules != null) {
authorityRules.putAll(rules);
}
RecordLog.info("[AuthorityRuleManager] Load authority rules: " + authorityRules);
}
- 配置规则转换
缓存规则格式为以规则应用的资源名为key,规则集合为value。配置规则数据源数据转换成该格式。
private Map<String, Set<AuthorityRule>> loadAuthorityConf(List<AuthorityRule> list) {
Map<String, Set<AuthorityRule>> newRuleMap = new ConcurrentHashMap<>();
if (list == null || list.isEmpty()) {
return newRuleMap;
}
for (AuthorityRule rule : list) {
if (!isValidRule(rule)) {
RecordLog.warn("[AuthorityRuleManager] Ignoring invalid authority rule when loading new rules: " + rule);
continue;
}
if (StringUtil.isBlank(rule.getLimitApp())) {
rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
}
String identity = rule.getResource();
Set<AuthorityRule> ruleSet = newRuleMap.get(identity);
// putIfAbsent
if (ruleSet == null) {
ruleSet = new HashSet<>();
ruleSet.add(rule);
newRuleMap.put(identity, ruleSet);
} else {
// One resource should only have at most one authority rule, so just ignore redundant rules.
RecordLog.warn("[AuthorityRuleManager] Ignoring redundant rule: " + rule.toString());
}
}
return newRuleMap;
}
1.4 配置规则校验AuthorityRuleChecker
- 1 获取当前资源请求来源,为空则不受限制。
- 2 若授权规则中的来源为空,则不做限制。
- 3 资源请求来源粗粒度indexof匹配授权规则中的来源配置
- 4 粗粒度匹配成功,则equals匹配
- 5 根据规则类型,匹配成功则黑名单禁止请求,白名单允许请求资源。匹配失败则相反。
static boolean passCheck(AuthorityRule rule, Context context) {
String requester = context.getOrigin();//1
if (StringUtil.isEmpty(requester) || StringUtil.isEmpty(rule.getLimitApp())) {//2
return true;
}
int pos = rule.getLimitApp().indexOf(requester);//3
boolean contain = pos > -1;
if (contain) {//4
boolean exactlyMatch = false;
String[] appArray = rule.getLimitApp().split(",");
for (String app : appArray) {//
if (requester.equals(app)) {
exactlyMatch = true;
break;
}
}
contain = exactlyMatch;
}
int strategy = rule.getStrategy();//5
if (strategy == RuleConstant.AUTHORITY_BLACK && contain) {
return false;
}
if (strategy == RuleConstant.AUTHORITY_WHITE && !contain) {
return false;
}
return true;
}
1.5 规则应用AuthoritySlot
- 获取所有黑白名单规则
- 获取资源名对应的规则集
- 通过配置规则校验函数AuthorityRuleChecker校验资源请求来源是否可以请求资源。失败则抛异常AuthorityException
void checkBlackWhiteAuthority(ResourceWrapper resource, Context context) throws AuthorityException {
Map<String, Set<AuthorityRule>> authorityRules = AuthorityRuleManager.getAuthorityRules();
if (authorityRules == null) {
return;
}
Set<AuthorityRule> rules = authorityRules.get(resource.getName());
if (rules == null) {
return;
}
for (AuthorityRule rule : rules) {
if (!AuthorityRuleChecker.passCheck(rule, context)) {
throw new AuthorityException(context.getOrigin(), rule);
}
}
}