本文主要研究一下sentinel的SentinelWebAutoConfigurationjava
spring-cloud-alibaba-sentinel-autoconfigure-0.2.0.BUILD-SNAPSHOT-sources.jar!/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.javanode
@Configuration @ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true) @EnableConfigurationProperties(SentinelProperties.class) public class SentinelWebAutoConfiguration { private static final Logger logger = LoggerFactory .getLogger(SentinelWebAutoConfiguration.class); @Value("${project.name:${spring.application.name:}}") private String projectName; @Autowired private SentinelProperties properties; public static final String APP_NAME = "project.name"; @PostConstruct private void init() { if (StringUtils.isEmpty(System.getProperty(APP_NAME))) { System.setProperty(APP_NAME, projectName); } if (StringUtils.isEmpty(System.getProperty(TransportConfig.SERVER_PORT))) { System.setProperty(TransportConfig.SERVER_PORT, properties.getPort()); } if (StringUtils.isEmpty(System.getProperty(TransportConfig.CONSOLE_SERVER))) { System.setProperty(TransportConfig.CONSOLE_SERVER, properties.getDashboard()); } } @Bean @ConditionalOnWebApplication public FilterRegistrationBean<Filter> servletRequestListener() { FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>(); SentinelProperties.Filter filterConfig = properties.getFilter(); if (null == filterConfig) { filterConfig = new SentinelProperties.Filter(); properties.setFilter(filterConfig); } 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])); Filter filter = new CommonFilter(); registration.setFilter(filter); registration.setOrder(filterConfig.getOrder()); logger.info("[Sentinel Starter] register Sentinel with urlPatterns: {}.", filterConfig.getUrlPatterns()); return registration; } }
sentinel-web-servlet-0.1.1-sources.jar!/com/alibaba/csp/sentinel/adapter/servlet/CommonFilter.javagit
public class CommonFilter implements Filter { @Override public void init(FilterConfig filterConfig) { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest sRequest = (HttpServletRequest)request; Entry entry = null; try { String target = FilterUtil.filterTarget(sRequest); target = WebCallbackManager.getUrlCleaner().clean(target); ContextUtil.enter(target); entry = SphU.entry(target, EntryType.IN); chain.doFilter(request, response); } catch (BlockException e) { HttpServletResponse sResponse = (HttpServletResponse)response; WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse); } catch (IOException e2) { Tracer.trace(e2); throw e2; } catch (ServletException e3) { Tracer.trace(e3); throw e3; } catch (RuntimeException e4) { Tracer.trace(e4); throw e4; } finally { if (entry != null) { entry.exit(); } ContextUtil.exit(); } } @Override public void destroy() { } }
sentinel-core-0.1.1-sources.jar!/com/alibaba/csp/sentinel/SphU.javagithub
/** * Checking all {@link Rule}s about the resource. * * @param name the unique name for the protected resource * @param type the resource is an inbound or an outbound method. This is used * to mark whether it can be blocked when the system is unstable, * only inbound traffic could be blocked by {@link SystemRule} * @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded. */ public static Entry entry(String name, EntryType type) throws BlockException { return Env.sph.entry(name, type, 1, OBJECTS0); }
sentinel-core-0.1.1-sources.jar!/com/alibaba/csp/sentinel/Env.javaweb
public class Env { public static final SlotsChainBuilder slotsChainbuilder = new DefaultSlotsChainBuilder(); public static final NodeBuilder nodeBuilder = new DefaultNodeBuilder(); public static final Sph sph = new CtSph(); static { // If init fails, the process will exit. InitExecutor.doInit(); } }
sentinel-core-0.1.1-sources.jar!/com/alibaba/csp/sentinel/init/InitExecutor.javaspring
/** * Load registered init functions and execute in order. * * @author Eric Zhao */ public final class InitExecutor { private static AtomicBoolean initialized = new AtomicBoolean(false); /** * If one {@link InitFunc} throws an exception, the init process * will immediately be interrupted and the application will exit. * * The initialization will be executed only once. */ public static void doInit() { if (!initialized.compareAndSet(false, true)) { return; } try { ServiceLoader<InitFunc> loader = ServiceLoader.load(InitFunc.class); List<OrderWrapper> initList = new ArrayList<OrderWrapper>(); for (InitFunc initFunc : loader) { RecordLog.info("[Sentinel InitExecutor] Found init func: " + initFunc.getClass().getCanonicalName()); insertSorted(initList, initFunc); } for (OrderWrapper w : initList) { w.func.init(); RecordLog.info(String.format("[Sentinel InitExecutor] Initialized: %s with order %d", w.func.getClass().getCanonicalName(), w.order)); } } catch (Exception ex) { RecordLog.info("[Sentinel InitExecutor] Init failed", ex); ex.printStackTrace(); System.exit(-1); } } private static void insertSorted(List<OrderWrapper> list, InitFunc func) { int order = resolveOrder(func); int idx = 0; for (; idx < list.size(); idx++) { if (list.get(idx).getOrder() > order) { break; } } list.add(idx, new OrderWrapper(order, func)); } private static int resolveOrder(InitFunc func) { if (!func.getClass().isAnnotationPresent(InitOrder.class)) { return InitOrder.LOWEST_PRECEDENCE; } else { return func.getClass().getAnnotation(InitOrder.class).value(); } } private InitExecutor() {} private static class OrderWrapper { private final int order; private final InitFunc func; OrderWrapper(int order, InitFunc func) { this.order = order; this.func = func; } int getOrder() { return order; } InitFunc getFunc() { return func; } } }
sentinel-core-0.1.1-sources.jar!/com/alibaba/csp/sentinel/CtSph.javaapi
/** * Do all {@link Rule}s checking about the resource. * * <p>Each distinct resource will use a {@link ProcessorSlot} to do rules checking. Same resource will use * same {@link ProcessorSlot} globally. </p> * * <p>Note that total {@link ProcessorSlot} count must not exceed {@link Constants#MAX_SLOT_CHAIN_SIZE}, * otherwise no rules checking will do. In this condition, all requests will pass directly, with no checking * or exception.</p> * * @param resourceWrapper resource name * @param count tokens needed * @param args arguments of user method call * @return {@link Entry} represents this call * @throws BlockException if any rule's threshold is exceeded */ public Entry entry(ResourceWrapper resourceWrapper, int count, Object... args) throws BlockException { Context context = ContextUtil.getContext(); if (context instanceof NullContext) { // Init the entry only. No rule checking will occur. return new CtEntry(resourceWrapper, null, context); } if (context == null) { context = MyContextUtil.myEnter(Constants.CONTEXT_DEFAULT_NAME, "", resourceWrapper.getType()); } // Global switch is close, no rule checking will do. if (!Constants.ON) { return new CtEntry(resourceWrapper, null, context); } ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper); /* * Means processor size exceeds {@link Constants.MAX_ENTRY_SIZE}, no * rule checking will do. */ if (chain == null) { return new CtEntry(resourceWrapper, null, context); } Entry e = new CtEntry(resourceWrapper, chain, context); try { chain.entry(context, resourceWrapper, null, count, args); } catch (BlockException e1) { e.exit(count, args); throw e1; } catch (Throwable e1) { RecordLog.info("sentinel unexpected exception", e1); } return e; } /** * Get {@link ProcessorSlotChain} of the resource. new {@link ProcessorSlotChain} will * be created if the resource doesn't relate one. * * <p>Same resource({@link ResourceWrapper#equals(Object)}) will share the same * {@link ProcessorSlotChain} globally, no matter in witch {@link Context}.<p/> * * <p> * Note that total {@link ProcessorSlot} count must not exceed {@link Constants#MAX_SLOT_CHAIN_SIZE}, * otherwise null will return. * </p> * * @param resourceWrapper target resource * @return {@link ProcessorSlotChain} of the resource */ private ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) { ProcessorSlotChain chain = chainMap.get(resourceWrapper); if (chain == null) { synchronized (LOCK) { chain = chainMap.get(resourceWrapper); if (chain == null) { // Entry size limit. if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) { return null; } chain = Env.slotsChainbuilder.build(); HashMap<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>( chainMap.size() + 1); newMap.putAll(chainMap); newMap.put(resourceWrapper, chain); chainMap = newMap; } } } return chain; }
sentinel-core-0.1.1-sources.jar!/com/alibaba/csp/sentinel/slots/DefaultSlotsChainBuilder.javaspringboot
/** * Helper class to create {@link ProcessorSlotChain}. * * @author qinan.qn * @author leyou */ public class DefaultSlotsChainBuilder implements SlotsChainBuilder { @Override public ProcessorSlotChain build() { ProcessorSlotChain chain = new DefaultProcessorSlotChain(); chain.addLast(new NodeSelectorSlot()); chain.addLast(new ClusterBuilderSlot()); chain.addLast(new LogSlot()); chain.addLast(new StatisticSlot()); chain.addLast(new SystemSlot()); chain.addLast(new AuthoritySlot()); chain.addLast(new FlowSlot()); chain.addLast(new DegradeSlot()); return chain; } }
這裏slot的整體架構以下: 架構