序
本文主要研究一下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时没有指定。