一、调用sentinel-core API方式
1、加入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.4.1</version>
</dependency>
2、使用
public static void main(String[] args) {
Entry entry = null;
try {
// 流控入口
entry = SphU.entry("HelloWorld");
// 业务逻辑.
System.out.println("hello world");
} catch (BlockException e1) {
// 被流控会流控异常,业务可以自己做相应的处理
System.out.println("blocked!");
} finally {
if (entry != null) {
entry.exit();
}
}
}
二、@SentinelResourceAspect注解方式
1、加入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>x.y.z</version>
</dependency>
2、使用
// 原函数
@SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
public String hello ( long s){
return String.format("Hello at %d", s);
}
// Fallback 函数,函数签名与原函数一致.
public String helloFallback (long s){
return String.format("Halooooo %d", s);
}
// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
public String exceptionHandler (long s, BlockException ex){
// Do some log here.
ex.printStackTrace();
return "Oops, error occurred at " + s;
}
3、原理
直接看下@SentinelResource的切面类SentinelResourceAspect
@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);
SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class);
if (annotation == null) {
// Should not go through here.
throw new IllegalStateException("Wrong state for SentinelResource annotation");
}
String resourceName = getResourceName(annotation.value(), originMethod);
EntryType entryType = annotation.entryType();
int resourceType = annotation.resourceType();
Entry entry = null;
try {
// 调用sentinel-core API
entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());
// 业务执行
return pjp.proceed();
} catch (BlockException ex) {
return handleBlockException(pjp, annotation, ex);
} catch (Throwable ex) {
Class<? extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();
// The ignore list will be checked first.
if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {
throw ex;
}
if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
traceException(ex);
return handleFallback(pjp, annotation, ex);
}
// No fallback function can handle the exception, so throw it out.
throw ex;
} finally {
if (entry != null) {
entry.exit(1, pjp.getArgs());
}
}
}
}
由SentinelResourceAspect切面类可知,其实就是对sentinel-core API的封装。
三、Spring Boot方式
1、加入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-boot-starter</artifactId>
<version>1.7.0.RELEASE</version>
</dependency>
2、原理
sentinel-spring-boot-starter依赖了sentinel-web-servlet-spring-boot-autoconfigure,这个autoconfigure的spring.factories中会自动注册SentinelWebMvcAutoConfiguration
@EnableConfigurationProperties({SentinelWebMvcProperties.class})
@EnableWebMvcCircuitBreaker
@ConditionalOnProperty(
value = {"sentinel.autoconfigure.enable"},
matchIfMissing = true,
havingValue = "true"
)
@Configuration
@Import({SentinelConfiguration.class})
public class SentinelWebMvcAutoConfiguration {
public SentinelWebMvcAutoConfiguration() {
}
}
@Import({SentinelFilterConfiguration.class, CompatibleSentinelConfiguration.class})
@Configuration
public class SentinelConfiguration {
public SentinelConfiguration() {
}
}
public class SentinelFilterConfiguration {
private final SentinelWebMvcProperties properties;
public SentinelFilterConfiguration(SentinelWebMvcProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean({CommonFilter.class})
public FilterRegistrationBean sentinel2Filter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
Filter filterConfig = this.properties.getFilter();
if (filterConfig.getUrlPatterns() == null || filterConfig.getUrlPatterns().isEmpty()) {
List<String> defaultPatterns = new ArrayList();
defaultPatterns.add("/*");
filterConfig.setUrlPatterns(defaultPatterns);
}
registration.addUrlPatterns((String[])filterConfig.getUrlPatterns().toArray(new String[0]));
// 注入Filter
javax.servlet.Filter filter = new CommonFilter();
registration.setFilter(filter);
registration.setOrder(filterConfig.getOrder());
registration.addInitParameter("HTTP_METHOD_SPECIFY", String.valueOf(this.properties.getHttpMethodSpecify()));
return registration;
}
}
看下CommonFilter类的继承关系:
由此就进入了Web Servlter模块,直接看下CommonFilter#doFilter
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);
boolean skipSentinel = this.detectSkipSentinel(target);
UrlCleaner urlCleaner = WebCallbackManager.getUrlCleaner();
if (urlCleaner != null) {
target = urlCleaner.clean(target);
}
if (!StringUtil.isEmpty(target) && !skipSentinel) {
String origin = this.parseOrigin(sRequest);
ContextUtil.enter(target, origin);
if (this.httpMethodSpecify) {
String pathWithHttpMethod = sRequest.getMethod().toUpperCase() + ":" + target;
urlEntry = SphU.entry(pathWithHttpMethod, 1, EntryType.IN);
} else {
// 调用sentinel-core API
urlEntry = SphU.entry(target, 1, EntryType.IN);
}
}
chain.doFilter(request, response);
} catch (BlockException var15) {
HttpServletResponse sResponse = (HttpServletResponse)response;
WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, var15);
} catch (ServletException | RuntimeException | IOException var16) {
Tracer.traceEntry(var16, urlEntry);
throw var16;
} finally {
if (urlEntry != null) {
urlEntry.exit();
}
ContextUtil.exit();
}
}
由此可知:其实也是对sentinel-core API的封装,默认会对所有的外部请求拦截,当然是否流控还要看有没有在Sentinel Dashboard配置限流规则!