Sentinel源码(流量入口)
发布于 2021-09-05 00:36
package com.alibaba.cloud.sentinel;
/*省略部分代码。。。。。。*/
public class SentinelWebAutoConfiguration {
/*省略部分代码。。。。。。*/
@Bean
@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled", matchIfMissing = true)
public FilterRegistrationBean sentinelFilter() {
/*Step 1 : 创建一个FilterRegistrationBean*/
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
SentinelProperties.Filter filterConfig = properties.getFilter();
if (filterConfig.getUrlPatterns() == null
|| filterConfig.getUrlPatterns().isEmpty()) {
List<String> defaultPatterns = new ArrayList<>();
defaultPatterns.add("/*");
filterConfig.setUrlPatterns(defaultPatterns);
}
registration.addUrlPatterns(filterConfig.getUrlPatterns().toArray(new String[0]));
/*重点Step 2 :创建一个CommonFilter*/
Filter filter = new CommonFilter();
/*Step 3 : 将CommonFilter放入FilterRegistrationBean*/
registration.setFilter(filter);
registration.setOrder(filterConfig.getOrder());
registration.addInitParameter("HTTP_METHOD_SPECIFY",
String.valueOf(properties.getHttpMethodSpecify()));
/*省略部分代码。。。。。。*/
return registration;
}
}
在SentinelWebAutoConfiguration被加载时,注入了一个FilterRegistrationBean的Bean对象。Step1创建一个注册Filter的FilterRegistrationBean对象registration;Step2创建了一个CommonFilter,这个后面重点研究;Step3将CommonFilter放入到filterChain中。
CommonFilter
package com.alibaba.csp.sentinel.adapter.servlet;
public class CommonFilter implements Filter {
/*省略部分代码。。。。。。*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
/*省略部分代码。。。。。。*/
try {
/*Step 1 :截取请求URL作为资源名赋予target */
String target = FilterUtil.filterTarget(sRequest);
/*省略部分代码。。。。。。*/
/*Step 2 :创建一个target名称的上下文*/
ContextUtil.enter(target, origin);
/*Step 3 :执行Sentinel的核心功能*/
entry = SphU.entry(target, EntryType.IN);
/*省略部分代码。。。。。。*/
chain.doFilter(request, response);
}
/*省略部分代码。。。。。。*/
ContextUtil.exit();
}
}
/*省略部分代码。。。。。。*/
}
CommonFilter会拦截所有进入Tomcat的请求,Step1截取请求中的URL作为“资源名”;Step2创建一个name为“资源名”的Context;Step3执行Sentinel的核心功能SphU.entry,这个后面重点研究。
package com.alibaba.cloud.sentinel.custom;
/*省略部分代码。。。。。。*/
public class SentinelAutoConfiguration {
/*省略部分代码。。。。。。*/
/*注入切面SentinelResourceAspect*/
@Bean
@ConditionalOnMissingBean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
![](https://weixin.aisoutu.com/cunchu4/4/2021-09-05/4_163077536419.png)
package com.alibaba.csp.sentinel.annotation.aspectj;
@Aspect
public class SentinelResourceAspect extends AbstractSentinelAspectSupport {
@Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)")
public void sentinelResourceAnnotationPointcut() {
}
@Around("sentinelResourceAnnotationPointcut()")
public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable {
Method originMethod = resolveMethod(pjp);
/*Step 1 :获取@SentinelResource注解*/
SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class);
/*Step 2 :获取@SentinelResource中的资源名*/
String resourceName = getResourceName(annotation.value(), originMethod);
EntryType entryType = annotation.entryType();
Entry entry = null;
try {
/*Step 3 :执行Sentinel核心功能*/
entry = SphU.entry(resourceName, entryType, 1, pjp.getArgs());
Object result = pjp.proceed();
return result;
} catch (BlockException ex) {
return handleBlockException(pjp, annotation, ex);
} catch (Throwable ex) {
traceException(ex, annotation);
throw ex;
} finally {
if (entry != null) {
entry.exit(1, pjp.getArgs());
}
}
}
}
通过上面SentinelWebAutoConfiguration和SentinelAutoConfiguration两个类的加载,成功将流量引入Sentinel的核心入口SphU.entry,下面我们对Sentinel的归一入口进行讲解。
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
throws BlockException {
/*Step 1-1 :创建上下文,如果在ThreadLocal中已存在,则直接返回上下文*/
Context context = ContextUtil.getContext();
/*省略部分代码。。。。。。*/
/*Step 1-2 :创建一条Slot Chain*/
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
/*省略部分代码。。。。。。*/
/*Step 2 :将Chain和当前Context结合*/
Entry e = new CtEntry(resourceWrapper, chain, context);
try {
/*Step 3 :Slot Chain入口,开始规则检查*/
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
e.exit(count, args);
throw e1;
} catch (Throwable e1) {
RecordLog.info("Sentinel unexpected exception", e1);
}
return e;
}
entryWithPriority是Sentinel的核心功能,Step1创建上下文Context、Slot Chain;Step2结合Context和Slot Chain;Step3进入Slot Chain进行规则检查并返回结果。
三、总结
Step1所有请求经过sentinel自定义的拦截器Sentinel CommonFilter;
Step2该拦截器从请求中提取URL作为资源名;
Step3进入Sentinel 核心的代码段entry.enter,至此引流工作已完成,后续交给Sentinel通用核心功能entryWithPriority;
Step4执行HandlerMapping映射处理机制,从上下文中挑选合适的HandlerMethod,即Controller中的@RequestMapping方法;
Step5进入方法前@SentinelResource拦截请求,利用AOP技术实现请求到被资源保护代码前的熔断限流功能;
本文来自网络或网友投稿,如有侵犯您的权益,请发邮件至:aisoutu@outlook.com 我们将第一时间删除。
相关素材