序
本文主要研究一下sentinel的AuthoritySlot
AuthoritySlot
com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java
public class AuthoritySlot extends AbstractLinkedProcessorSlot {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args)
throws Throwable {
AuthorityRuleManager.checkAuthority(resourceWrapper, context, node, count);
fireEntry(context, resourceWrapper, node, count, args);
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
fireExit(context, resourceWrapper, count, args);
}
}
- 这里执行AuthorityRuleManager.checkAuthority进行校验
AuthorityRuleManager
com/alibaba/csp/sentinel/slots/block/authority/AuthorityRuleManager.java
public class AuthorityRuleManager {
private static Map> authorityRules
= new ConcurrentHashMap>();
final static RulePropertyListener listener = new RulePropertyListener();
private static SentinelProperty> currentProperty
= new DynamicSentinelProperty>();
static {
currentProperty.addListener(listener);
}
public static void register2Property(SentinelProperty> property) {
synchronized (listener) {
if (currentProperty != null) {
currentProperty.removeListener(listener);
}
property.addListener(listener);
currentProperty = property;
}
}
/**
* Load the authority rules to memory.
*
* @param rules list of authority rules
*/
public static void loadRules(List rules) {
currentProperty.updateValue(rules);
}
public static void checkAuthority(ResourceWrapper resource, Context context, DefaultNode node, int count)
throws BlockException {
if (authorityRules == null) {
return;
}
List rules = authorityRules.get(resource.getName());
if (rules == null) {
return;
}
for (AuthorityRule rule : rules) {
if (!rule.passCheck(context, node, count)) {
throw new AuthorityException(context.getOrigin());
}
}
}
public static boolean hasConfig(String resource) {
return authorityRules.containsKey(resource);
}
/**
* Get a copy of the rules.
*
* @return a new copy of the rules.
*/
public static List getRules() {
List rules = new ArrayList();
if (authorityRules == null) {
return rules;
}
for (Map.Entry> entry : authorityRules.entrySet()) {
rules.addAll(entry.getValue());
}
return rules;
}
private static class RulePropertyListener implements PropertyListener> {
@Override
public void configUpdate(List conf) {
Map> rules = loadAuthorityConf(conf);
authorityRules.clear();
if (rules != null) {
authorityRules.putAll(rules);
}
RecordLog.info("[AuthorityRuleManager] Authority rules received: " + authorityRules);
}
private Map> loadAuthorityConf(List list) {
if (list == null) {
return null;
}
Map> newRuleMap = new ConcurrentHashMap>();
for (AuthorityRule rule : list) {
if (StringUtil.isBlank(rule.getLimitApp())) {
rule.setLimitApp(FlowRule.LIMIT_APP_DEFAULT);
}
String identity = rule.getResource();
List ruleM = newRuleMap.get(identity);
// putIfAbsent
if (ruleM == null) {
ruleM = new ArrayList();
ruleM.add(rule);
newRuleMap.put(identity, ruleM);
} 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;
}
@Override
public void configLoad(List value) {
Map> rules = loadAuthorityConf(value);
authorityRules.clear();
if (rules != null) {
authorityRules.putAll(rules);
}
RecordLog.info("[AuthorityRuleManager] Load authority rules: " + authorityRules);
}
}
}
- checkAuthority方法从authorityRules中获取对应资源的规则,之后挨个进行rule.passCheck校验
AuthorityRule
com/alibaba/csp/sentinel/slots/block/authority/AuthorityRule.java
public class AuthorityRule extends AbstractRule {
/**
* Mode: 0 for whitelist; 1 for blacklist.
*/
private int strategy = RuleConstant.AUTHORITY_WHITE;
public int getStrategy() {
return strategy;
}
public void setStrategy(int strategy) {
this.strategy = strategy;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (!(o instanceof AuthorityRule)) { return false; }
if (!super.equals(o)) { return false; }
AuthorityRule rule = (AuthorityRule)o;
return strategy == rule.strategy;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + strategy;
return result;
}
@Override
public boolean passCheck(Context context, DefaultNode node, int count, Object... args) {
String requester = context.getOrigin();
// Empty origin or empty limitApp will pass.
if (StringUtil.isEmpty(requester) || StringUtil.isEmpty(this.getLimitApp())) {
return true;
}
// Do exact match with origin name.
int pos = this.getLimitApp().indexOf(requester);
boolean contain = pos > -1;
if (contain) {
boolean exactlyMatch = false;
String[] appArray = this.getLimitApp().split(",");
for (String app : appArray) {
if (requester.equals(app)) {
exactlyMatch = true;
break;
}
}
contain = exactlyMatch;
}
if (strategy == RuleConstant.AUTHORITY_BLACK && contain) {
return false;
}
if (strategy == RuleConstant.AUTHORITY_WHITE && !contain) {
return false;
}
return true;
}
@Override
public String toString() {
return "AuthorityRule{" +
"resource=" + getResource() +
", limitApp=" + getLimitApp() +
", strategy=" + strategy +
"} ";
}
}
- passCheck方法通过context的origin跟limitApp进行匹配
CommonFilter
com/alibaba/csp/sentinel/adapter/servlet/CommonFilter.java
public class CommonFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest sRequest = (HttpServletRequest)request;
Entry entry = null;
try {
String target = FilterUtil.filterTarget(sRequest);
target = WebCallbackManager.getUrlCleaner().clean(target);
ContextUtil.enter(target);
entry = SphU.entry(target, EntryType.IN);
chain.doFilter(request, response);
} catch (BlockException e) {
HttpServletResponse sResponse = (HttpServletResponse)response;
WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse);
} catch (IOException e2) {
Tracer.trace(e2);
throw e2;
} catch (ServletException e3) {
Tracer.trace(e3);
throw e3;
} catch (RuntimeException e4) {
Tracer.trace(e4);
throw e4;
} finally {
if (entry != null) {
entry.exit();
}
ContextUtil.exit();
}
}
@Override
public void destroy() {
}
}
- 目前基于servlet的CommonFilter,在调用ContextUtil.enter(target)的时候,并没有指定origin,目前默认为""
小结
AuthoritySlot主要用来做黑白名单的匹配,现在还不是很完善,origin在CommonFilter进入entry时没有指定。
doc
- AuthoritySlot