基于sentinel 1.8.6
从sentinel-dashboard来看,sentinel主要提供了流控,熔断,热点,系统规则,授权规则等。
针对http请求的数据监控以及规则限制的适配,可以参考sentinel-spring-webmvc-adapter
以及sentinel-web-servlet-adapter
具体的实现类
com.alibaba.csp.sentinel.adapter.servlet.CommonFilter
com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor
一个是基于spring的HandlerInterceptor,一个是基于servlet的Filter。
以com.alibaba.csp.sentinel.adapter.servlet.CommonFilter
为例
init,doFilter,destory这三个方法分别对应初始化,逻辑处理,执行完处理的逻辑,重点放在doFilter()方法。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest sRequest = (HttpServletRequest) request;
Entry urlEntry = null;
try {
String target = FilterUtil.filterTarget(sRequest);
// Clean and unify the URL.
// For REST APIs, you have to clean the URL (e.g. `/foo/1` and `/foo/2` -> `/foo/:id`), or
// the amount of context and resources will exceed the threshold.
UrlCleaner urlCleaner = WebCallbackManager.getUrlCleaner();
if (urlCleaner != null) {
target = urlCleaner.clean(target);
}
// If you intend to exclude some URLs, you can convert the URLs to the empty string ""
// in the UrlCleaner implementation.
if (!StringUtil.isEmpty(target)) {
//解析来源
String origin = parseOrigin(sRequest);
String contextName = webContextUnify ? WebServletConfig.WEB_SERVLET_CONTEXT_NAME : target;
//设置上下文
ContextUtil.enter(contextName, origin);
//执行sentinel一系列规则,各种校验。
if (httpMethodSpecify) {
// Add HTTP method prefix if necessary.
String pathWithHttpMethod = sRequest.getMethod().toUpperCase() + COLON + target;
urlEntry = SphU.entry(pathWithHttpMethod, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
} else {
urlEntry = SphU.entry(target, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
}
}
//真正的执行请求
chain.doFilter(request, response);
} catch (BlockException e) {
HttpServletResponse sResponse = (HttpServletResponse) response;
// Return the block page, or redirect to another URL.
WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e);
} catch (IOException | ServletException | RuntimeException e2) {
Tracer.traceEntry(e2, urlEntry);
throw e2;
} finally {
if (urlEntry != null) {
urlEntry.exit();
}
ContextUtil.exit();
}
}
主要看下在执行真正的chain.doFilter()执行前,sentinel都做了些什么?
com.alibaba.csp.sentinel.SphU#entry(java.lang.String, int, com.alibaba.csp.sentinel.EntryType)
com.alibaba.csp.sentinel.CtSph#entryWithPriority(com.alibaba.csp.sentinel.slotchain.ResourceWrapper, int, boolean, java.lang.Object...)
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
throws BlockException {
Context context = ContextUtil.getContext();
if (context instanceof NullContext) {
// The {@link NullContext} indicates that the amount of context has exceeded the threshold,
// so here init the entry only. No rule checking will be done.
return new CtEntry(resourceWrapper, null, context);
}
if (context == null) {
// Using default context.
context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
}
// Global switch is close, no rule checking will do.
if (!Constants.ON) {
return new CtEntry(resourceWrapper, null, context);
}
//初始化流控规则的chain链
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
/*
* Means amount of resources (slot chain) exceeds {@link Constants.MAX_SLOT_CHAIN_SIZE},
* so no rule checking will be done.
*/
if (chain == null) {
return new CtEntry(resourceWrapper, null, context);
}
Entry e = new CtEntry(resourceWrapper, chain, context);
try {
//执行链
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
e.exit(count, args);
throw e1;
} catch (Throwable e1) {
// This should not happen, unless there are errors existing in Sentinel internal.
RecordLog.info("Sentinel unexpected exception", e1);
}
return e;
}
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper); 这个方法会初始化默认的规则校验链,说白了就是流控,熔断...这些规则,我该怎么一个一个的进行校验。
com.alibaba.csp.sentinel.slotchain.SlotChainProvider#newSlotChain
com.alibaba.csp.sentinel.spi.SpiLoader#loadFirstInstanceOrDefault
sentinel基于SPI的方式实现了一个默认的处理链,接口为com.alibaba.csp.sentinel.slotchain.SlotChainBuilder
的实现
处理链的每一个slot都是实现ProcessorSolt。
以熔断为例,她是一个扩展的spi接口,并且设置了优先级,所以初始化后的规则校验链如下图所示
到目前为止,大致清楚了,我们在sentinel设置了一些类型的规则,规则校验的顺序是什么?怎么实现,如何加载的应该已经很清晰了。
再回到com.alibaba.csp.sentinel.CtSph#entryWithPriority(com.alibaba.csp.sentinel.slotchain.ResourceWrapper, int, boolean, java.lang.Object...)
这个方法中,执行的具体方法为com.alibaba.csp.sentinel.slotchain.ProcessorSlot#entry
以流控校验为例
执行flowSlot的entry方法时,会从上下文中获取该资源的统计信息。并与当前类型的规则进行比较,并更新进统计信息。
统计信息存储在StatisticNode里。包括当前资源名,执行了通过多少次,拒绝多少次等信息。
com.alibaba.csp.sentinel.node.StatisticNode
以上大致就是http请求,sentinel处理的流程,对于dubbo请求,参考类
com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboProviderFilter
com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboConsumerFilter
dubbo支持应用本身作为消费者以及生产者的调用信息的统计。