1、注册
首先执行SentinelAutoConfiguration的init方法。
@PostConstruct
private void init() {
//设置系统属性 csp.sentinel.log.dir
if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_DIR))
&& StringUtils.hasText(properties.getLog().getDir())) {
System.setProperty(LogBase.LOG_DIR, properties.getLog().getDir());
}
//csp.sentinel.log.use.pid
if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_NAME_USE_PID))
&& properties.getLog().isSwitchPid()) {
System.setProperty(LogBase.LOG_NAME_USE_PID,
String.valueOf(properties.getLog().isSwitchPid()));
}
//csp.sentinel.app.name
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.APP_NAME_PROP_KEY))
&& StringUtils.hasText(projectName)) {
System.setProperty(SentinelConfig.APP_NAME_PROP_KEY, projectName);
}
//csp.sentinel.api.port
if (StringUtils.isEmpty(System.getProperty(TransportConfig.SERVER_PORT))
&& StringUtils.hasText(properties.getTransport().getPort())) {
System.setProperty(TransportConfig.SERVER_PORT,
properties.getTransport().getPort());
}
//csp.sentinel.dashboard.server
if (StringUtils.isEmpty(System.getProperty(TransportConfig.CONSOLE_SERVER))
&& StringUtils.hasText(properties.getTransport().getDashboard())) {
System.setProperty(TransportConfig.CONSOLE_SERVER,
properties.getTransport().getDashboard());
}
//csp.sentinel.heartbeat.interval.ms
if (StringUtils.isEmpty(System.getProperty(TransportConfig.HEARTBEAT_INTERVAL_MS))
&& StringUtils
.hasText(properties.getTransport().getHeartbeatIntervalMs())) {
System.setProperty(TransportConfig.HEARTBEAT_INTERVAL_MS,
properties.getTransport().getHeartbeatIntervalMs());
}
//csp.sentinel.heartbeat.client.ip
if (StringUtils.isEmpty(System.getProperty(TransportConfig.HEARTBEAT_CLIENT_IP))
&& StringUtils.hasText(properties.getTransport().getClientIp())) {
System.setProperty(TransportConfig.HEARTBEAT_CLIENT_IP,
properties.getTransport().getClientIp());
}
//csp.sentinel.charset
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.CHARSET))
&& StringUtils.hasText(properties.getMetric().getCharset())) {
System.setProperty(SentinelConfig.CHARSET,
properties.getMetric().getCharset());
}
//csp.sentinel.metric.file.single.size
if (StringUtils
.isEmpty(System.getProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE))
&& StringUtils.hasText(properties.getMetric().getFileSingleSize())) {
System.setProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE,
properties.getMetric().getFileSingleSize());
}
//csp.sentinel.metric.file.total.count
if (StringUtils
.isEmpty(System.getProperty(SentinelConfig.TOTAL_METRIC_FILE_COUNT))
&& StringUtils.hasText(properties.getMetric().getFileTotalCount())) {
System.setProperty(SentinelConfig.TOTAL_METRIC_FILE_COUNT,
properties.getMetric().getFileTotalCount());
}
//csp.sentinel.flow.cold.factor
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.COLD_FACTOR))
&& StringUtils.hasText(properties.getFlow().getColdFactor())) {
System.setProperty(SentinelConfig.COLD_FACTOR,
properties.getFlow().getColdFactor());
}
//csp.sentinel.web.servlet.block.page
if (StringUtils.hasText(properties.getBlockPage())) {
setConfig(BLOCK_PAGE_URL_CONF_KEY, properties.getBlockPage());
}
// earlier initialize 是否需要提前初始化 这个放后面。
if (properties.isEager()) {
InitExecutor.doInit();
}
}
然后是SentinelAutoConfiguration的sentinelBeanPostProcessor
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
@ConditionalOnProperty(name = "resttemplate.sentinel.enabled", havingValue = "true",
matchIfMissing = true)
public SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext);
}
这个bean只是 简单的将applicationContext赋值给成员变量。
SentinelWebFluxAutoConfiguration的构造方法。
因为这里是集成了gatway。
//三个成员变量
//private final List<ViewResolver> viewResolvers;
//private final ServerCodecConfigurer serverCodecConfigurer;
//@Autowired
//private Optional<BlockRequestHandler> blockRequestHandler;
public SentinelWebFluxAutoConfiguration(
ObjectProvider<List<ViewResolver>> viewResolvers,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolvers.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@PostConstruct
public void init() {
blockRequestHandler.ifPresent(WebFluxCallbackManager::setBlockHandler);
}
SentinelAutoConfiguration.SentinelConverterConfiguration
@ConditionalOnClass(ObjectMapper.class)
@Configuration(proxyBeanMethods = false)
protected static class SentinelConverterConfiguration {
@Configuration(proxyBeanMethods = false)
protected static class SentinelJsonConfiguration {
private ObjectMapper objectMapper = new ObjectMapper();
public SentinelJsonConfiguration() {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
}
@Bean("sentinel-json-flow-converter")
public JsonConverter jsonFlowConverter() {
return new JsonConverter(objectMapper, FlowRule.class);
}
@Bean("sentinel-json-degrade-converter")
public JsonConverter jsonDegradeConverter() {
return new JsonConverter(objectMapper, DegradeRule.class);
}
@Bean("sentinel-json-system-converter")
public JsonConverter jsonSystemConverter() {
return new JsonConverter(objectMapper, SystemRule.class);
}
@Bean("sentinel-json-authority-converter")
public JsonConverter jsonAuthorityConverter() {
return new JsonConverter(objectMapper, AuthorityRule.class);
}
@Bean("sentinel-json-param-flow-converter")
public JsonConverter jsonParamFlowConverter() {
return new JsonConverter(objectMapper, ParamFlowRule.class);
}
}
SentinelAutoConfiguration
@Bean
@ConditionalOnMissingBean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
这里注册了一个切面类,用来对标注@SentinelResource 注解的方法进行增强。
@Bean
@ConditionalOnMissingBean
public SentinelDataSourceHandler sentinelDataSourceHandler(
DefaultListableBeanFactory beanFactory, SentinelProperties sentinelProperties,
Environment env) {
return new SentinelDataSourceHandler(beanFactory, sentinelProperties, env);
}
这里是读取配置中心配置的Sentinel的配置。
2、Sentinel的切面
注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
String value() default "";
//IN 和 OUT 输入绑定和输出绑定
EntryType entryType() default EntryType.OUT;
int resourceType() default 0;
String blockHandler() default "";
Class<?>[] blockHandlerClass() default {};
String fallback() default "";
String defaultFallback() default "";
Class<?>[] fallbackClass() default {};
Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};
Class<? extends Throwable>[] exceptionsToIgnore() default {};
}
切面类
@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");
}
//获取资源名称,如果注解资源名称为空,则资源名称为 className:methodName(参数类型名称以逗号分割)
String resourceName = getResourceName(annotation.value(), originMethod);
//获取entryType
EntryType entryType = annotation.entryType();
//获取resourceType
int resourceType = annotation.resourceType();
Entry entry = null;
try {
//生成一个Entry
entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());
//执行原有方法
Object result = pjp.proceed();
return result;
} 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;
}
//抛出的异常是注解中 exceptionsToTrace 规定的异常,执行fullback方法
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 {
//方法执行完,entry退出
if (entry != null) {
entry.exit(1, pjp.getArgs());
}
}
}
}
环绕通知,请求的出入会进行相应的一些操作。
创建entry
//com.alibaba.csp.sentinel.SphU#entry
public static Entry entry(String name, int resourceType, EntryType trafficType, Object[] args)
throws BlockException {
return Env.sph.entryWithType(name, resourceType, trafficType, 1, args);
}
Env类
public class Env {
public static final Sph sph = new CtSph();
static {
//这里会初始化拉取线程,如果eager为false,为true的话会在自动配置类里面初始化
// If init fails, the process will exit.
InitExecutor.doInit();
}
}
根据类型创建entry
//com.alibaba.csp.sentinel.CtSph#entryWithType
public Entry entryWithType(String name, int resourceType, EntryType entryType, int count, Object[] args)
throws BlockException {
return entryWithType(name, resourceType, entryType, count, false, args);
}
//com.alibaba.csp.sentinel.CtSph
public Entry entryWithType(String name, int resourceType, EntryType entryType, int count, boolean prioritized,
Object[] args) throws BlockException {
//生成一个resouceWrapper 对资源的一个包装
StringResourceWrapper resource = new StringResourceWrapper(name, entryType, resourceType);
return entryWithPriority(resource, count, prioritized, args);
}
entry的具体创建逻辑在下一篇。