天天看點

Sentinel源碼分析十一、Spring內建Sentinel

本文主要分析spring-cloud-starter-alibaba-sentinel中的源碼,看看springcloud alibaba環境下內建sentinel做了些什麼,實際上已經超出了Sentinel自身部分了。

首先既然是springcloud,那麼配置入口先找spring.factories檔案中的配置

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.sentinel.SentinelWebAutoConfiguration,\
com.alibaba.cloud.sentinel.SentinelWebFluxAutoConfiguration,\
com.alibaba.cloud.sentinel.endpoint.SentinelEndpointAutoConfiguration,\
com.alibaba.cloud.sentinel.custom.SentinelAutoConfiguration,\
com.alibaba.cloud.sentinel.feign.SentinelFeignAutoConfiguration

org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
com.alibaba.cloud.sentinel.custom.SentinelCircuitBreakerConfiguration
           

這裡我們看幾個配置類

SentinelWebAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
@ConditionalOnClass(SentinelWebInterceptor.class)
@EnableConfigurationProperties(SentinelProperties.class)
public class SentinelWebAutoConfiguration implements WebMvcConfigurer {

   private static final Logger log = LoggerFactory
         .getLogger(SentinelWebAutoConfiguration.class);

   @Autowired
   private SentinelProperties properties;

   @Autowired
   private Optional<UrlCleaner> urlCleanerOptional;

   @Autowired
   private Optional<BlockExceptionHandler> blockExceptionHandlerOptional;

   @Autowired
   private Optional<RequestOriginParser> requestOriginParserOptional;

   @Autowired
   private Optional<SentinelWebInterceptor> sentinelWebInterceptorOptional;

   @Override
   public void addInterceptors(InterceptorRegistry registry) {
      if (!sentinelWebInterceptorOptional.isPresent()) {
         return;
      }
      SentinelProperties.Filter filterConfig = properties.getFilter();
      registry.addInterceptor(sentinelWebInterceptorOptional.get())
            .order(filterConfig.getOrder())
            .addPathPatterns(filterConfig.getUrlPatterns());
      log.info(
            "[Sentinel Starter] register SentinelWebInterceptor with urlPatterns: {}.",
            filterConfig.getUrlPatterns());
   }

   @Bean
   @ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled",
         matchIfMissing = true)
   public SentinelWebInterceptor sentinelWebInterceptor(
         SentinelWebMvcConfig sentinelWebMvcConfig) {
      return new SentinelWebInterceptor(sentinelWebMvcConfig);
   }

   @Bean
   @ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled",
         matchIfMissing = true)
   public SentinelWebMvcConfig sentinelWebMvcConfig() {
      SentinelWebMvcConfig sentinelWebMvcConfig = new SentinelWebMvcConfig();
      sentinelWebMvcConfig.setHttpMethodSpecify(properties.getHttpMethodSpecify());

      if (blockExceptionHandlerOptional.isPresent()) {
         blockExceptionHandlerOptional
               .ifPresent(sentinelWebMvcConfig::setBlockExceptionHandler);
      }
      else {
         if (StringUtils.hasText(properties.getBlockPage())) {
            sentinelWebMvcConfig.setBlockExceptionHandler(((request, response,
                  e) -> response.sendRedirect(properties.getBlockPage())));
         }
         else {
            sentinelWebMvcConfig
                  .setBlockExceptionHandler(new DefaultBlockExceptionHandler());
         }
      }

      urlCleanerOptional.ifPresent(sentinelWebMvcConfig::setUrlCleaner);
      requestOriginParserOptional.ifPresent(sentinelWebMvcConfig::setOriginParser);
      return sentinelWebMvcConfig;
   }

}
           

實作了WebMvcConfigurer接口。addInterceptors方法根據spring.cloud.sentinel.filter配置添加攔截器。

攔截器是SentinelWebInterceptor,是Sentinel-Adapter裡的了。看一下它繼承的abstract類AbstractSentinelInterceptor:

public abstract class AbstractSentinelInterceptor implements HandlerInterceptor 
           

實作攔截器接口,preHandle方法:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {
    try {
        String resourceName = getResourceName(request);

        if (StringUtil.isNotEmpty(resourceName)) {
            // Parse the request origin using registered origin parser.
            String origin = parseOrigin(request);
            ContextUtil.enter(SENTINEL_SPRING_WEB_CONTEXT_NAME, origin);
            Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);

            setEntryInRequest(request, baseWebMvcConfig.getRequestAttributeName(), entry);
        }
        return true;
    } catch (BlockException e) {
        handleBlockException(request, response, e);
        return false;
    }
}
           

可以看到,在filter指定的攔截範圍内,執行entry方法。

SentinelAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
@EnableConfigurationProperties(SentinelProperties.class)
public class SentinelAutoConfiguration {

   @Value("${project.name:${spring.application.name:}}")
   private String projectName;

   @Autowired
   private SentinelProperties properties;

   @PostConstruct
   private void init() {

      // 系統配置
      ... 省略部分代碼

      // earlier initialize
      // 初始化操作
      // InitFunc SPI的實作
      if (properties.isEager()) {
         InitExecutor.doInit();
      }

   }

   @Bean
   @ConditionalOnMissingBean
   public SentinelResourceAspect sentinelResourceAspect() {
      // 基于@SentinelResource注解的Aspect,有@SentinelResource注解的則認為是資源,會進行entry攔截。
      return new SentinelResourceAspect();
   }

   @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
   @ConditionalOnMissingBean
   public SentinelDataSourceHandler sentinelDataSourceHandler(
         DefaultListableBeanFactory beanFactory, SentinelProperties sentinelProperties,
         Environment env) {
      return new SentinelDataSourceHandler(beanFactory, sentinelProperties, env);
   }

    // 轉換器配置
   @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);
         }

      }

      @ConditionalOnClass(XmlMapper.class)
      @Configuration(proxyBeanMethods = false)
      protected static class SentinelXmlConfiguration {

         private XmlMapper xmlMapper = new XmlMapper();

         public SentinelXmlConfiguration() {
            xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
                  false);
         }

         @Bean("sentinel-xml-flow-converter")
         public XmlConverter xmlFlowConverter() {
            return new XmlConverter(xmlMapper, FlowRule.class);
         }

         @Bean("sentinel-xml-degrade-converter")
         public XmlConverter xmlDegradeConverter() {
            return new XmlConverter(xmlMapper, DegradeRule.class);
         }

         @Bean("sentinel-xml-system-converter")
         public XmlConverter xmlSystemConverter() {
            return new XmlConverter(xmlMapper, SystemRule.class);
         }

         @Bean("sentinel-xml-authority-converter")
         public XmlConverter xmlAuthorityConverter() {
            return new XmlConverter(xmlMapper, AuthorityRule.class);
         }

         @Bean("sentinel-xml-param-flow-converter")
         public XmlConverter xmlParamFlowConverter() {
            return new XmlConverter(xmlMapper, ParamFlowRule.class);
         }

      }

   }

}
           
  1. @PostConstruct方法,初始化Sentinel環境,執行InitFunc的init
  2. SentinelResourceAspec
    @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 {
                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;
                }
                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());
                }
            }
        }
    }
               
    被@SentinelResource注解的添加了個Aspect,Around通知器。調用entry。
  3. SentinelBeanPostProcessor

    實作了MergedBeanDefinitionPostProcessor接口

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
          Class<?> beanType, String beanName) {
       if (checkSentinelProtect(beanDefinition, beanType, beanName)) {
          SentinelRestTemplate sentinelRestTemplate;
          if (beanDefinition.getSource() instanceof StandardMethodMetadata) {
             sentinelRestTemplate = ((StandardMethodMetadata) beanDefinition
                   .getSource()).getIntrospectedMethod()
                         .getAnnotation(SentinelRestTemplate.class);
          }
          else {
             sentinelRestTemplate = beanDefinition.getResolvedFactoryMethod()
                   .getAnnotation(SentinelRestTemplate.class);
          }
          // check class and method validation
          checkSentinelRestTemplate(sentinelRestTemplate, beanName);
          cache.put(beanName, sentinelRestTemplate);
       }
    }
               
    被@SentinelRestTemplate注解标注的,加到緩存裡
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
          throws BeansException {
       if (beanName != null && cache.containsKey(beanName)) {
          // add interceptor for each RestTemplate with @SentinelRestTemplate annotation
          StringBuilder interceptorBeanNamePrefix = new StringBuilder();
          SentinelRestTemplate sentinelRestTemplate = cache.get(beanName);
          interceptorBeanNamePrefix
                .append(StringUtils.uncapitalize(
                      SentinelProtectInterceptor.class.getSimpleName()))
                .append("_")
                .append(sentinelRestTemplate.blockHandlerClass().getSimpleName())
                .append(sentinelRestTemplate.blockHandler()).append("_")
                .append(sentinelRestTemplate.fallbackClass().getSimpleName())
                .append(sentinelRestTemplate.fallback()).append("_")
                .append(sentinelRestTemplate.urlCleanerClass().getSimpleName())
                .append(sentinelRestTemplate.urlCleaner());
          RestTemplate restTemplate = (RestTemplate) bean;
          String interceptorBeanName = interceptorBeanNamePrefix + "@"
                + bean.toString();
          registerBean(interceptorBeanName, sentinelRestTemplate, (RestTemplate) bean);
          SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext
                .getBean(interceptorBeanName, SentinelProtectInterceptor.class);
          restTemplate.getInterceptors().add(0, sentinelProtectInterceptor);
       }
       return bean;
    }
               
    RestTemplate添加了一個攔截器
    restTemplate.getInterceptors().add(0, sentinelProtectInterceptor);
               

    看一下這個攔截器的方法:

    com.alibaba.cloud.sentinel.custom.SentinelProtectInterceptor#intercept

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body,
          ClientHttpRequestExecution execution) throws IOException {
       URI uri = request.getURI();
       String hostResource = request.getMethod().toString() + ":" + uri.getScheme()
             + "://" + uri.getHost()
             + (uri.getPort() == -1 ? "" : ":" + uri.getPort());
       String hostWithPathResource = hostResource + uri.getPath();
       boolean entryWithPath = true;
       if (hostResource.equals(hostWithPathResource)) {
          entryWithPath = false;
       }
       Method urlCleanerMethod = BlockClassRegistry.lookupUrlCleaner(
             sentinelRestTemplate.urlCleanerClass(),
             sentinelRestTemplate.urlCleaner());
       if (urlCleanerMethod != null) {
          hostWithPathResource = (String) methodInvoke(urlCleanerMethod,
                hostWithPathResource);
       }
    
       Entry hostEntry = null;
       Entry hostWithPathEntry = null;
       ClientHttpResponse response = null;
       try {
          hostEntry = SphU.entry(hostResource, EntryType.OUT);
          if (entryWithPath) {
             hostWithPathEntry = SphU.entry(hostWithPathResource, EntryType.OUT);
          }
          response = execution.execute(request, body);
          if (this.restTemplate.getErrorHandler().hasError(response)) {
             Tracer.trace(
                   new IllegalStateException("RestTemplate ErrorHandler has error"));
          }
       }
       catch (Throwable e) {
          if (!BlockException.isBlockException(e)) {
             Tracer.trace(e);
          }
          else {
             return handleBlockException(request, body, execution, (BlockException) e);
          }
       }
       finally {
          if (hostWithPathEntry != null) {
             hostWithPathEntry.exit();
          }
          if (hostEntry != null) {
             hostEntry.exit();
          }
       }
       return response;
    }
               

    好的,已經看到entry方法了。

    總結這個處理器SentinelBeanPostProcessor的作用:

    對于使用RestTemplate進行資源請求的,如果在定義的RestTemplate對象上增加@SentinelRestTemplate注解,則會添加一個SentinelProtectInterceptor攔截器,對restTemplate的請求進行攔截,通過entry實作限流、降級等操作。比如:

    @Bean
    @SentinelRestTemplate
    public RestTemplate restTemplate(){
         return new RestTemplate();
    }
               
    此restTemplate的調用會entry
  4. SentinelDataSourceHandler

    實作了SmartInitializingSingleton接口,看一下afterSingletonsInstantiated方法

    @Override
    public void afterSingletonsInstantiated() {
       sentinelProperties.getDatasource()
             .forEach((dataSourceName, dataSourceProperties) -> {
                try {
                   List<String> validFields = dataSourceProperties.getValidField();
                   if (validFields.size() != 1) {
                      log.error("[Sentinel Starter] DataSource " + dataSourceName
                            + " multi datasource active and won't loaded: "
                            + dataSourceProperties.getValidField());
                      return;
                   }
                   AbstractDataSourceProperties abstractDataSourceProperties = dataSourceProperties
                         .getValidDataSourceProperties();
                   abstractDataSourceProperties.setEnv(env);
                   abstractDataSourceProperties.preCheck(dataSourceName);
                   registerBean(abstractDataSourceProperties, dataSourceName
                         + "-sentinel-" + validFields.get(0) + "-datasource");
                }
                catch (Exception e) {
                   log.error("[Sentinel Starter] DataSource " + dataSourceName
                         + " build error: " + e.getMessage(), e);
                }
             });
    }
    
    private void registerBean(final AbstractDataSourceProperties dataSourceProperties,
    			String dataSourceName) {
        Map<String, Object> propertyMap = Arrays
            .stream(dataSourceProperties.getClass().getDeclaredFields())
            .collect(HashMap::new, (m, v) -> {
                try {
                    v.setAccessible(true);
                    m.put(v.getName(), v.get(dataSourceProperties));
                }
                catch (IllegalAccessException e) {
                    log.error("[Sentinel Starter] DataSource " + dataSourceName
                              + " field: " + v.getName() + " invoke error");
                    throw new RuntimeException(
                        "[Sentinel Starter] DataSource " + dataSourceName
                        + " field: " + v.getName() + " invoke error",
                        e);
                }
            }, HashMap::putAll);
        propertyMap.put(CONVERTER_CLASS_FIELD, dataSourceProperties.getConverterClass());
        propertyMap.put(DATA_TYPE_FIELD, dataSourceProperties.getDataType());
    
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
            .genericBeanDefinition(dataSourceProperties.getFactoryBeanName());
    
        propertyMap.forEach((propertyName, propertyValue) -> {
            Field field = ReflectionUtils.findField(dataSourceProperties.getClass(),
                                                    propertyName);
            if (null == field) {
                return;
            }
            if (DATA_TYPE_FIELD.equals(propertyName)) {
                String dataType = StringUtils.trimAllWhitespace(propertyValue.toString());
                if (CUSTOM_DATA_TYPE.equals(dataType)) {
                    try {
                        if (StringUtils
                            .isEmpty(dataSourceProperties.getConverterClass())) {
                            throw new RuntimeException("[Sentinel Starter] DataSource "
                                                       + dataSourceName
                                                       + "dataType is custom, please set converter-class "
                                                       + "property");
                        }
                        // construct custom Converter with 'converterClass'
                        // configuration and register
                        String customConvertBeanName = "sentinel-"
                            + dataSourceProperties.getConverterClass();
                        if (!this.beanFactory.containsBean(customConvertBeanName)) {
                            this.beanFactory.registerBeanDefinition(customConvertBeanName,
                                                                    BeanDefinitionBuilder
                                                                    .genericBeanDefinition(
                                                                        Class.forName(dataSourceProperties
                                                                                      .getConverterClass()))
                                                                    .getBeanDefinition());
                        }
                        builder.addPropertyReference("converter", customConvertBeanName);
                    }
                    catch (ClassNotFoundException e) {
                        log.error("[Sentinel Starter] DataSource " + dataSourceName
                                  + " handle "
                                  + dataSourceProperties.getClass().getSimpleName()
                                  + " error, class name: "
                                  + dataSourceProperties.getConverterClass());
                        throw new RuntimeException("[Sentinel Starter] DataSource "
                                                   + dataSourceName + " handle "
                                                   + dataSourceProperties.getClass().getSimpleName()
                                                   + " error, class name: "
                                                   + dataSourceProperties.getConverterClass(), e);
                    }
                }
                else {
                    if (!dataTypeList.contains(
                        StringUtils.trimAllWhitespace(propertyValue.toString()))) {
                        throw new RuntimeException("[Sentinel Starter] DataSource "
                                                   + dataSourceName + " dataType: " + propertyValue
                                                   + " is not support now. please using these types: "
                                                   + dataTypeList.toString());
                    }
                    // converter type now support xml or json.
                    // The bean name of these converters wrapped by
                    // 'sentinel-{converterType}-{ruleType}-converter'
                    builder.addPropertyReference("converter",
                                                 "sentinel-" + propertyValue.toString() + "-"
                                                 + dataSourceProperties.getRuleType().getName()
                                                 + "-converter");
                }
            }
            else if (CONVERTER_CLASS_FIELD.equals(propertyName)) {
                return;
            }
            else {
                // wired properties
                Optional.ofNullable(propertyValue)
                    .ifPresent(v -> builder.addPropertyValue(propertyName, v));
            }
        });
    
        this.beanFactory.registerBeanDefinition(dataSourceName,
                                                builder.getBeanDefinition());
        // init in Spring
        AbstractDataSource newDataSource = (AbstractDataSource) this.beanFactory
            .getBean(dataSourceName);
    
        // register property in RuleManager
        dataSourceProperties.postRegister(newDataSource);
    }
               
    就是注冊DataSource的Bean定義。前文提到的規則持久化部分。

SentinelFeignAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ SphU.class, Feign.class })
public class SentinelFeignAutoConfiguration {

   @Bean
   @Scope("prototype")
   @ConditionalOnMissingBean
   @ConditionalOnProperty(name = "feign.sentinel.enabled")
   public Feign.Builder feignSentinelBuilder() {
      return SentinelFeign.builder();
   }

}
           

Sentinel與Feign的內建。

看一下builder

@Override
public Feign build() {
   super.invocationHandlerFactory(new InvocationHandlerFactory() {
      @Override
      public InvocationHandler create(Target target,
            Map<Method, MethodHandler> dispatch) {
         // using reflect get fallback and fallbackFactory properties from
         // FeignClientFactoryBean because FeignClientFactoryBean is a package
         // level class, we can not use it in our package
         Object feignClientFactoryBean = Builder.this.applicationContext
               .getBean("&" + target.type().getName());

         Class fallback = (Class) getFieldValue(feignClientFactoryBean,
               "fallback");
         Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean,
               "fallbackFactory");
         String beanName = (String) getFieldValue(feignClientFactoryBean,
               "contextId");
         if (!StringUtils.hasText(beanName)) {
            beanName = (String) getFieldValue(feignClientFactoryBean, "name");
         }

         Object fallbackInstance;
         FallbackFactory fallbackFactoryInstance;
         // check fallback and fallbackFactory properties
         if (void.class != fallback) {
            fallbackInstance = getFromContext(beanName, "fallback", fallback,
                  target.type());
            return new SentinelInvocationHandler(target, dispatch,
                  new FallbackFactory.Default(fallbackInstance));
         }
         if (void.class != fallbackFactory) {
            fallbackFactoryInstance = (FallbackFactory) getFromContext(
                  beanName, "fallbackFactory", fallbackFactory,
                  FallbackFactory.class);
            return new SentinelInvocationHandler(target, dispatch,
                  fallbackFactoryInstance);
         }
         return new SentinelInvocationHandler(target, dispatch);
      }

      private Object getFromContext(String name, String type,
            Class fallbackType, Class targetType) {
         Object fallbackInstance = feignContext.getInstance(name,
               fallbackType);
         if (fallbackInstance == null) {
            throw new IllegalStateException(String.format(
                  "No %s instance of type %s found for feign client %s",
                  type, fallbackType, name));
         }

         if (!targetType.isAssignableFrom(fallbackType)) {
            throw new IllegalStateException(String.format(
                  "Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
                  type, fallbackType, targetType, name));
         }
         return fallbackInstance;
      }
   });

   super.contract(new SentinelContractHolder(contract));
   return super.build();
}
           

建立一個InvocationHandler,這裡是SentinelInvocationHandler:

看一下它的invoke方法:

@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
      throws Throwable {
   ...省略部分代碼
   Object result;
   MethodHandler methodHandler = this.dispatch.get(method);
   // only handle by HardCodedTarget
   if (target instanceof Target.HardCodedTarget) {
      Target.HardCodedTarget hardCodedTarget = (Target.HardCodedTarget) target;
      MethodMetadata methodMetadata = SentinelContractHolder.METADATA_MAP
            .get(hardCodedTarget.type().getName()
                  + Feign.configKey(hardCodedTarget.type(), method));
      // resource default is HttpMethod:protocol://url
      if (methodMetadata == null) {
         result = methodHandler.invoke(args);
      }
      else {
         String resourceName = methodMetadata.template().method().toUpperCase()
               + ":" + hardCodedTarget.url() + methodMetadata.template().path();
         Entry entry = null;
         try {
            ContextUtil.enter(resourceName);
            entry = SphU.entry(resourceName, EntryType.OUT, 1, args);
            result = methodHandler.invoke(args);
         }
         catch (Throwable ex) {
            // fallback handle
            if (!BlockException.isBlockException(ex)) {
               Tracer.trace(ex);
            }
            if (fallbackFactory != null) {
               try {
                  Object fallbackResult = fallbackMethodMap.get(method)
                        .invoke(fallbackFactory.create(ex), args);
                  return fallbackResult;
               }
               catch (IllegalAccessException e) {
                  // shouldn't happen as method is public due to being an
                  // interface
                  throw new AssertionError(e);
               }
               catch (InvocationTargetException e) {
                  throw new AssertionError(e.getCause());
               }
            }
            else {
               // throw exception if fallbackFactory is null
               throw ex;
            }
         }
         finally {
            if (entry != null) {
               entry.exit(1, args);
            }
            ContextUtil.exit();
         }
      }
   }
   else {
      // other target type using default strategy
      result = methodHandler.invoke(args);
   }

   return result;
}
           

好了,看到entry調用了。 在執行feign調用前entry。

SentinelWebFluxAutoConfiguration

WebFlux的內建配置。