這裏涉及了spring的註解驅動. 自動配置等相關知識
@EnableZuulProxy-> @import(ZuulProxyMarkerConfiguration.class)-> @Bean就是初始化了Marker類. 至關於打標記java
經過Maker類 找到了zuul的自動配置類ZuulProxyAutoConfiguration和父類ZuulServerAutoConfiguration 並在MATA-INF/spring.factories裏面找到了這兩個類的配置項:spring
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration,\ org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration
這兩個類的加載都是經過@EnableAutoConfiguration完成的.segmentfault
@Configuration //啓動zuul屬性. 能夠理解爲加載ZuulProperties @EnableConfigurationProperties({ ZuulProperties.class }) //條件轉載. 須要依賴ZuulServlet和ZuulServletFilter類. 也就是說要依賴zuul-core @ConditionalOnClass({ ZuulServlet.class, ZuulServletFilter.class }) //上下文環境中必須存在Marker這個Bean. @ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class) public class ZuulServerAutoConfiguration { @Bean // 缺乏zuulServlet Bean時加載 @ConditionalOnMissingBean(name = "zuulServlet") // yml文件中配置的屬性zuul.use-filter = false或者沒有配置時加載 @ConditionalOnProperty(name = "zuul.use-filter", havingValue = "false", matchIfMissing = true) public ServletRegistrationBean zuulServlet() { ServletRegistrationBean<ZuulServlet> servlet = new ServletRegistrationBean<>( new ZuulServlet(), this.zuulProperties.getServletPattern()); // The whole point of exposing this servlet is to provide a route that doesn't // buffer requests. servlet.addInitParameter("buffer-requests", "false"); return servlet; } @Bean @ConditionalOnMissingBean(name = "zuulServletFilter") //yml文件中配置的屬性zuul.use-filter = true. 必需要有這個配置還必須是true 纔會加載. @ConditionalOnProperty(name = "zuul.use-filter", havingValue = "true", matchIfMissing = false) public FilterRegistrationBean zuulServletFilter() { final FilterRegistrationBean<ZuulServletFilter> filterRegistration = new FilterRegistrationBean<>(); filterRegistration.setUrlPatterns( Collections.singleton(this.zuulProperties.getServletPattern())); filterRegistration.setFilter(new ZuulServletFilter()); filterRegistration.setOrder(Ordered.LOWEST_PRECEDENCE); // The whole point of exposing this servlet is to provide a route that doesn't // buffer requests. filterRegistration.addInitParameter("buffer-requests", "false"); return filterRegistration; } }
ZuulServlet類和ZuulServletFilter類是zuul提供的兩種啓動方式, 對應了servlet和servlet Filter.設計模式
servlet指南
這個類告訴了zuul filter的執行順序app
提供了一些 方便的API. 好比 HashMap<String, String[]> getParameters()等負載均衡
首先是單例.jsp
public class FilterProcessor { public Object runFilters(String sType) throws Throwable { if (RequestContext.getCurrentContext().debugRouting()) { Debug.addRoutingDebug("Invoking {" + sType + "} type filters"); } boolean bResult = false; List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType); if (list != null) { for (int i = 0; i < list.size(); i++) { ZuulFilter zuulFilter = list.get(i); Object result = processZuulFilter(zuulFilter); if (result != null && result instanceof Boolean) { bResult |= ((Boolean) result); } } } return bResult; } }
FilterLoader.getInstance().getFiltersByType(sType); 是獲取sType類型的filter. 並按照優先級進行排序.
其背後調用了FilterRegistry這個類 這個類很簡單, 維護了一個ConcurrentHashMap<String, ZuulFilter> filters 容器.
這兩個類的初始化都是在ZuulServerAutoConfiguration這個自動裝載的ide
@Configuration protected static class ZuulFilterConfiguration { @Autowired private Map<String, ZuulFilter> filters; @Bean public ZuulFilterInitializer zuulFilterInitializer(CounterFactory counterFactory, TracerFactory tracerFactory) { FilterLoader filterLoader = FilterLoader.getInstance(); FilterRegistry filterRegistry = FilterRegistry.instance(); return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory, filterLoader, filterRegistry); } }
這個filters屬性時如何初始化的. 業務本身定義的filter只要交給spring託管, 就能夠加載進來.post
優先級 -3.ui
優先級 -2
優先級 -1
執行條件: application/x-www-form-urlencoded 或者 multipart/form-data 時候執行
優先級 1
執行條件: 請求中的debug參數(該參數能夠經過zuul.debug.parameter來自定義)爲true,或者配置參數zuul.debug.request爲true時執行
優先級 5
執行條件: RequestContext不存在forward.to和serviceId兩個參數時執行
public class PreDecorationFilter extends ZuulFilter { @Override public Object run() { //獲取請求上下文 RequestContext ctx = RequestContext.getCurrentContext(); //獲取請求路徑 final String requestURI = this.urlPathHelper .getPathWithinApplication(ctx.getRequest()); //獲取路由信息(CompositeRouteLocator 實在自動裝配階段裝配的) Route route = this.routeLocator.getMatchingRoute(requestURI); // 路由存在 if (route != null) { //獲取路由的定位信息(url或者serviceId) String location = route.getLocation(); if (location != null) { //設置requestURI= path ctx.put(REQUEST_URI_KEY, route.getPath()); //設置proxy = routeId ctx.put(PROXY_KEY, route.getId()); //不存在自定義的敏感頭信息 設置默認的 ("Cookie", "Set-Cookie", "Authorization") if (!route.isCustomSensitiveHeaders()) { this.proxyRequestHelper.addIgnoredHeaders( this.properties.getSensitiveHeaders().toArray(new String[0])); } //存在 就用用戶本身定義的 else { this.proxyRequestHelper.addIgnoredHeaders( route.getSensitiveHeaders().toArray(new String[0])); } //設置重試屬性 if (route.getRetryable() != null) { ctx.put(RETRYABLE_KEY, route.getRetryable()); } //若是location以http或https開頭,將其添加到RequestContext的routeHost中,在RequestContext的originResponseHeaders中添加X-Zuul-Service與location的鍵值對; if (location.startsWith(HTTP_SCHEME + ":") || location.startsWith(HTTPS_SCHEME + ":")) { ctx.setRouteHost(getUrl(location)); ctx.addOriginResponseHeader(SERVICE_HEADER, location); } //若是location以forward:開頭,則將其添加到RequestContext的forward.to中,將RequestContext的routeHost設置爲null並返回; else if (location.startsWith(FORWARD_LOCATION_PREFIX)) { ctx.set(FORWARD_TO_KEY, StringUtils.cleanPath( location.substring(FORWARD_LOCATION_PREFIX.length()) + route.getPath())); ctx.setRouteHost(null); return null; } //不然將location添加到RequestContext的serviceId中,將RequestContext的routeHost設置爲null,在RequestContext的originResponseHeaders中添加X-Zuul-ServiceId與location的鍵值對。 else { // set serviceId for use in filters.route.RibbonRequest ctx.set(SERVICE_ID_KEY, location); ctx.setRouteHost(null); ctx.addOriginResponseHeader(SERVICE_ID_HEADER, location); } //若是zuul.addProxyHeaders=true 則在RequestContext的zuulRequestHeaders中添加一系列請求頭:X-Forwarded-Host、X-Forwarded-Port、X-Forwarded-Proto、X-Forwarded-Prefix、X-Forwarded-For if (this.properties.isAddProxyHeaders()) { addProxyHeaders(ctx, route); String xforwardedfor = ctx.getRequest() .getHeader(X_FORWARDED_FOR_HEADER); String remoteAddr = ctx.getRequest().getRemoteAddr(); if (xforwardedfor == null) { xforwardedfor = remoteAddr; } else if (!xforwardedfor.contains(remoteAddr)) { // Prevent duplicates xforwardedfor += ", " + remoteAddr; } ctx.addZuulRequestHeader(X_FORWARDED_FOR_HEADER, xforwardedfor); } //若是zuul.addHostHeader=ture 則在則在RequestContext的zuulRequestHeaders中添加host if (this.properties.isAddHostHeader()) { ctx.addZuulRequestHeader(HttpHeaders.HOST, toHostHeader(ctx.getRequest())); } } } //若是 route=null 在RequestContext中將forward.to設置爲forwardURI,默認狀況下forwardURI爲請求路徑。 else { log.warn("No route found for uri: " + requestURI); String forwardURI = getForwardUri(requestURI); ctx.set(FORWARD_TO_KEY, forwardURI); } return null; } }
優先級 10
執行條件: RequestContext中的routeHost爲null,serviceId不爲null。sendZuulResponse=true. 即只對經過serviceId配置路由規則的請求生效
使用Ribbon和Hystrix來向服務實例發起請求,並將服務實例的請求結果返回
優先級 100
執行條件: RequestContext中的routeHost不爲null。即只對經過url配置路由規則的請求生效
直接向routeHost參數的物理地址發起請求,該請求是直接經過httpclient包實現的,而沒有使用Hystrix命令進行包裝,因此這類請求並無線程隔離和熔斷器的保護。
優先級 500
執行條件: RequestContext中的forward.to不爲null。即用來處理路由規則中的forward本地跳轉配置
優先級 1000
執行條件: 沒有拋出異常,RequestContext中的throwable屬性爲null(若是不爲null說明已經被error過濾器處理過了,這裏的post過濾器就不須要處理了),而且RequestContext中zuulResponseHeaders、responseDataStream、responseBody三者有同樣不爲null(說明實際請求的響應不爲空)。
優先級 SendResponseFilter - 100
執行條件: HttpStatus.valueOf(statusCode).is3xxRedirection() 響應碼是3XX的時候執行
功能: 將Location信息轉化爲Zuul URL.
優先級 0
執行條件:RequestContext中的throwable不爲null,且sendErrorFilter.ran屬性爲false。
在request中設置javax.servlet.error.status_code、javax.servlet.error.exception、javax.servlet.error.message三個屬性。將RequestContext中的sendErrorFilter.ran屬性設置爲true。而後組織成一個forward到API網關/error錯誤端點的請求來產生錯誤響應。
@Configuration //加載這個4個類 @Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class, RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class, RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class, HttpClientConfiguration.class }) @ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class) public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration { @Bean @ConditionalOnMissingBean(RibbonRoutingFilter.class) public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper, RibbonCommandFactory<?> ribbonCommandFactory) { //ribbonCommandFactory這個具體是什麼類型 取決import導入的類 RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory, this.requestCustomizers); return filter; } @Bean @ConditionalOnMissingBean({ SimpleHostRoutingFilter.class, CloseableHttpClient.class }) public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties zuulProperties, ApacheHttpClientConnectionManagerFactory connectionManagerFactory, ApacheHttpClientFactory httpClientFactory) { return new SimpleHostRoutingFilter(helper, zuulProperties, connectionManagerFactory, httpClientFactory); } }
ZuulProxyAutoConfiguration自動配置類.
是ZuulServerAutoConfiguration的子類, 導入了 RestClientRibbonConfiguration OkHttpRibbonConfiguration HttpClientRibbonConfiguration三個自動配置.
SpringClientFactory
zuulProperties zuul的配置項
zuulFallbackProviders Hystrix回調
在RibbonRoutingFilter看到了zuul在何時啓動ribbon的. 同時出現了RibbonCommand類. 這是實現了HystrixExecutable. 這個類是能夠理解爲Hystrix的執行器.
RibbonCommand有四個子類. 一個抽象類AbstractRibbonCommand和三個實現類 分別是依據httpClient實現的和OkHttp實現的以及RestClient實現的.
這裏使用了設計模式抽象工廠方法模式. RibbonCommandFactory工廠類接口 AbstractRibbonCommandFactory抽象類. 功能是記錄FallbackProvider. 至關於註冊表.用map記錄.
三種RibbonCommand建立都有各自工廠去構建.
// 以okHttp爲例子 public class RibbonCommandFactoryConfiguration { @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnRibbonOkHttpClientCondition.class) @interface ConditionalOnRibbonOkHttpClient { } @Configuration // ribbon.okhttp.enabled yml文件中存在這個屬性 @ConditionalOnRibbonOkHttpClient // 環境中存在這個類okhttp3.OkHttpClient @ConditionalOnClass(name = "okhttp3.OkHttpClient") protected static class OkHttpRibbonConfiguration { @Autowired(required = false) //FallbackProvider的全部實現類 必須添加註解@Component. 在這裏能夠組裝完成. private Set<FallbackProvider> zuulFallbackProviders = Collections.emptySet(); // @Bean 註冊到Spring IOC容器中. @Bean @ConditionalOnMissingBean public RibbonCommandFactory<?> ribbonCommandFactory( SpringClientFactory clientFactory, ZuulProperties zuulProperties) { return new OkHttpRibbonCommandFactory(clientFactory, zuulProperties, zuulFallbackProviders); } } private static class OnRibbonOkHttpClientCondition extends AnyNestedCondition { OnRibbonOkHttpClientCondition() { super(ConfigurationPhase.PARSE_CONFIGURATION); } @ConditionalOnProperty("ribbon.okhttp.enabled") static class RibbonProperty { } } }
其餘的實現也是相同的套路.
ribbon.restclient.enabled 使用RestClientRibbonCommandFactory
ribbon.okhttp.enabled 使用OkHttpRibbonCommandFactory
ribbon.httpclient.enabled matchIfMissing=true 意思是當不設置任何值的時候,默認初始HttpClientRibbonCommandFactory
在結合ZuulProxyAutoConfiguration類中@Bean RibbonRoutingFilter的構建. 在項目啓動完成後,
RibbonRoutingFilter經過RibbonCommandFactory.create()方法. 是能夠構建出RibbonCommand類的.
protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception { Map<String, Object> info = this.helper.debug(context.getMethod(), context.getUri(), context.getHeaders(), context.getParams(), context.getRequestEntity()); // 這裏的邏輯就清楚了. RibbonCommand command = this.ribbonCommandFactory.create(context); try { ClientHttpResponse response = command.execute(); this.helper.appendDebug(info, response.getRawStatusCode(), response.getHeaders()); return response; } catch (HystrixRuntimeException ex) { return handleException(info, ex); } }
super(zuulFallbackProviders); -> AbstractRibbonCommandFactory()
public abstract class AbstractRibbonCommandFactory implements RibbonCommandFactory { private Map<String, FallbackProvider> fallbackProviderCache; private FallbackProvider defaultFallbackProvider = null; public AbstractRibbonCommandFactory(Set<FallbackProvider> fallbackProviders) { this.fallbackProviderCache = new HashMap<>(); for (FallbackProvider provider : fallbackProviders) { String route = provider.getRoute(); // 若是route設置的是* 或者 不設置. 就將這個FallbackProvider設置爲默認的 if ("*".equals(route) || route == null) { defaultFallbackProvider = provider; } // 其餘的按照route值 註冊到map中. else { fallbackProviderCache.put(route, provider); } } } protected FallbackProvider getFallbackProvider(String route) { // 獲取的時候 若是在map中不存在. 就使用默認的 FallbackProvider provider = fallbackProviderCache.get(route); if (provider == null) { provider = defaultFallbackProvider; } return provider; } }
public class OkHttpRibbonCommandFactory extends AbstractRibbonCommandFactory { private SpringClientFactory clientFactory; private ZuulProperties zuulProperties; public OkHttpRibbonCommandFactory(SpringClientFactory clientFactory, ZuulProperties zuulProperties) { this(clientFactory, zuulProperties, Collections.<FallbackProvider>emptySet()); } public OkHttpRibbonCommandFactory(SpringClientFactory clientFactory, ZuulProperties zuulProperties, Set<FallbackProvider> zuulFallbackProviders) { super(zuulFallbackProviders); this.clientFactory = clientFactory; this.zuulProperties = zuulProperties; } @Override public OkHttpRibbonCommand create(final RibbonCommandContext context) { //這個不解釋 final String serviceId = context.getServiceId(); //依據服務ID得到FallbackProvider FallbackProvider fallbackProvider = getFallbackProvider(serviceId); //建立負載均衡客戶端 final OkHttpLoadBalancingClient client = this.clientFactory.getClient(serviceId, OkHttpLoadBalancingClient.class); // 設置負載均衡 client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId)); //建立OkHttpRibbonCommand實例, return new OkHttpRibbonCommand(serviceId, client, context, zuulProperties, fallbackProvider, clientFactory.getClientConfig(serviceId)); } } public OkHttpRibbonCommand(final String commandKey, final OkHttpLoadBalancingClient client, final RibbonCommandContext context, final ZuulProperties zuulProperties, final FallbackProvider zuulFallbackProvider, final IClientConfig config) { //調用父類的構造方法 super(commandKey, client, context, zuulProperties, zuulFallbackProvider, config); } public AbstractRibbonCommand(String commandKey, LBC client, RibbonCommandContext context, ZuulProperties zuulProperties, FallbackProvider fallbackProvider, IClientConfig config) { //getSetter 設置Hystrix的屬性值 this(getSetter(commandKey, zuulProperties, config), client, context, fallbackProvider, config); } protected static Setter getSetter(final String commandKey, ZuulProperties zuulProperties, IClientConfig config) { // @formatter:off commandKey= serviceId 每一個CommandKey表明一個依賴抽象,相同的依賴要使用相同的CommandKey名稱。依賴隔離的根本就是對相同CommandKey的依賴作隔離. //CommandGroup 命令分組用於對依賴操做分組,便於統計,彙總等. Setter commandSetter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RibbonCommand")) .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey)); // 構建了策略和超時時間 final HystrixCommandProperties.Setter setter = createSetter(config, commandKey, zuulProperties); //信號量方式 if (zuulProperties.getRibbonIsolationStrategy() == ExecutionIsolationStrategy.SEMAPHORE) { final String name = ZuulConstants.ZUUL_EUREKA + commandKey + ".semaphore.maxSemaphores"; // we want to default to semaphore-isolation since this wraps // 2 others commands that are already thread isolated // 獲取信號量大小 默認值100 final DynamicIntProperty value = DynamicPropertyFactory.getInstance() .getIntProperty(name, zuulProperties.getSemaphore().getMaxSemaphores()); setter.withExecutionIsolationSemaphoreMaxConcurrentRequests(value.get()); } //線程池方式 else if (zuulProperties.getThreadPool().isUseSeparateThreadPools()) { //每一個serviceId一個線程池 final String threadPoolKey = zuulProperties.getThreadPool().getThreadPoolKeyPrefix() + commandKey; commandSetter.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(threadPoolKey)); } return commandSetter.andCommandPropertiesDefaults(setter); // @formatter:on } protected static HystrixCommandProperties.Setter createSetter(IClientConfig config, String commandKey, ZuulProperties zuulProperties) { //設置Hystrix超時時間 int hystrixTimeout = getHystrixTimeout(config, commandKey); //設置策略 ribbon的默認策略是信息量 return HystrixCommandProperties.Setter() .withExecutionIsolationStrategy( zuulProperties.getRibbonIsolationStrategy()) .withExecutionTimeoutInMilliseconds(hystrixTimeout); } protected static int getHystrixTimeout(IClientConfig config, String commandKey) { //獲取Ribbon的超時時間 int ribbonTimeout = getRibbonTimeout(config, commandKey); DynamicPropertyFactory dynamicPropertyFactory = DynamicPropertyFactory .getInstance(); // 默認的超時時間 int defaultHystrixTimeout = dynamicPropertyFactory.getIntProperty( "hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds", 0).get(); // 獲取針對serviceId設置的超時時間 int commandHystrixTimeout = dynamicPropertyFactory .getIntProperty("hystrix.command." + commandKey + ".execution.isolation.thread.timeoutInMilliseconds", 0) .get(); int hystrixTimeout; // 若是設置了serverId的超時時間 就用serverId if (commandHystrixTimeout > 0) { hystrixTimeout = commandHystrixTimeout; } // 不然查看默認的超時時間 else if (defaultHystrixTimeout > 0) { hystrixTimeout = defaultHystrixTimeout; } // 若是最後都沒有設置, 就用ribbon的 else { hystrixTimeout = ribbonTimeout; } // 能夠理解爲 設置了用serviceId的用serverid的,不然用Hystrix默認的.若是都沒設置用ribbon設置的 if (hystrixTimeout < ribbonTimeout) { LOGGER.warn("The Hystrix timeout of " + hystrixTimeout + "ms for the command " + commandKey + " is set lower than the combination of the Ribbon read and connect timeout, " + ribbonTimeout + "ms."); } return hystrixTimeout; } protected static int getRibbonTimeout(IClientConfig config, String commandKey) { int ribbonTimeout; //如何用戶沒有自定義使用系統默認的 2000ms if (config == null) { ribbonTimeout = RibbonClientConfiguration.DEFAULT_READ_TIMEOUT + RibbonClientConfiguration.DEFAULT_CONNECT_TIMEOUT; } //讀取用戶設置的 else { int ribbonReadTimeout = getTimeout(config, commandKey, "ReadTimeout", IClientConfigKey.Keys.ReadTimeout, RibbonClientConfiguration.DEFAULT_READ_TIMEOUT); int ribbonConnectTimeout = getTimeout(config, commandKey, "ConnectTimeout", IClientConfigKey.Keys.ConnectTimeout, RibbonClientConfiguration.DEFAULT_CONNECT_TIMEOUT); int maxAutoRetries = getTimeout(config, commandKey, "MaxAutoRetries", IClientConfigKey.Keys.MaxAutoRetries, DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES); int maxAutoRetriesNextServer = getTimeout(config, commandKey, "MaxAutoRetriesNextServer", IClientConfigKey.Keys.MaxAutoRetriesNextServer, DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER); ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1); } return ribbonTimeout; } private static int getTimeout(IClientConfig config, String commandKey, String property, IClientConfigKey<Integer> configKey, int defaultValue) { DynamicPropertyFactory dynamicPropertyFactory = DynamicPropertyFactory .getInstance(); return dynamicPropertyFactory .getIntProperty(commandKey + "." + config.getNameSpace() + "." + property, config.get(configKey, defaultValue)) .get(); }
當OkHttpRibbonCommand建立完成後, 這些數據就都設置完成了,