springCloud(六)

接着學習,之前學習過了feign和hystrix源碼,發現還是有些概念不是很清晰,需要加強自身的基礎。

這一篇學習zuul。

爲了使用zuul,我們需要新建一個module,

,只需要在啓動類上增加註解



即可。

配置文件中除了配置關鍵的zone,常規的timeout等,最關鍵的就是zuul的route配置。這裏配置如下:

zuul:
  routes:
    feign:
      path: /app1/**
      serviceId: feign
    another:
      path: /app2/**
      serviceId: another

這裏配置對應的bean爲ZuulProperties,

可以看到有一個變量LikedHashMap,保存了所有的route信息。

	/**
	 * Map of route names to properties.
	 */
	private Map<String, ZuulRoute> routes = new LinkedHashMap<>();


ZuulRoute保存了上述對應的配置文件內容:

public static class ZuulRoute {

		/**
		 * The ID of the route (the same as its map key by default).
		 */
		private String id;

		/**
		 * The path (pattern) for the route, e.g. /foo/**.
		 */
		private String path;

		/**
		 * The service ID (if any) to map to this route. You can specify a physical URL or
		 * a service, but not both.
		 */
		private String serviceId;

		/**
		 * A full physical URL to map to the route. An alternative is to use a service ID
		 * and service discovery to find the physical address.
		 */
		private String url;

		/**
		 * Flag to determine whether the prefix for this route (the path, minus pattern
		 * patcher) should be stripped before forwarding.
		 */
		private boolean stripPrefix = true;

		/**
		 * Flag to indicate that this route should be retryable (if supported). Generally
		 * retry requires a service ID and ribbon.
		 */
		private Boolean retryable;

		/**
		 * List of sensitive headers that are not passed to downstream requests. Defaults
		 * to a "safe" set of headers that commonly contain user credentials. It's OK to
		 * remove those from the list if the downstream service is part of the same system
		 * as the proxy, so they are sharing authentication data. If using a physical URL
		 * outside your own domain, then generally it would be a bad idea to leak user
		 * credentials.
		 */
		private Set<String> sensitiveHeaders = new LinkedHashSet<>();

		private boolean customSensitiveHeaders = false;

對應了配置文件中的內容。

zuul關鍵的配置在於@EnableZuulProxy。

@EnableCircuitBreaker
@EnableDiscoveryClient
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyConfiguration.class)
public @interface EnableZuulProxy {
}


我們可以看到該配置包含了EnableDiscoveryClient,因爲zuul轉發請求也需要到服務中心獲取服務,同樣也需要把自身註冊到服務中心。

該註解引入了類ZuulProxyConfiguration,該類加載了zuul的基本的配置,包括負載均衡,服務註冊和發現,filter等。
負載均衡的Ribbon

@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
		RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
		RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class }

在它的父類ZuulConfiguration中配置了上面所說的ZuulProperties配置,基本的filter等。

@Autowired
protected ZuulProperties zuulProperties;

@Autowired
protected ServerProperties server;

默認的pre filter:

@Bean
	public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) {
		return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(), this.zuulProperties,
				proxyRequestHelper);
	}


// pre filters

	@Bean
	public ServletDetectionFilter servletDetectionFilter() {
		return new ServletDetectionFilter();
	}

	@Bean
	public FormBodyWrapperFilter formBodyWrapperFilter() {
		return new FormBodyWrapperFilter();
	}

	@Bean
	public DebugFilter debugFilter() {
		return new DebugFilter();
	}

	@Bean
	public Servlet30WrapperFilter servlet30WrapperFilter() {
		return new Servlet30WrapperFilter();
	}


默認的route filter:

	// route filters
	@Bean
	public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
			RibbonCommandFactory<?> ribbonCommandFactory) {
		RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory, this.requestCustomizers);
		return filter;
	}

	@Bean
	public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties zuulProperties) {
		return new SimpleHostRoutingFilter(helper, zuulProperties);	
	}
    @Bean
	public SendForwardFilter sendForwardFilter() {
		return new SendForwardFilter();
	}


默認的post filter:

// post filters

	@Bean
	public SendResponseFilter sendResponseFilter() {
		return new SendResponseFilter();
	}

	@Bean
	public SendErrorFilter sendErrorFilter() {
		return new SendErrorFilter();
	}

其中所有的filter初始化如下:

@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);
		}

	}


FilterRegistry是一個單例,其中包含了一個CurrentHashMap,保存了所有的zuulFilter,並提供了基本的查詢,刪除等方法。
在FilterLoader中持有FilterRegistry對象,做了一些基本操作的包裝,對外提供操作。


最關鍵的是注入了zuul的核心servlet,

 @Bean
	@ConditionalOnMissingBean(name = "zuulServlet")
	public ServletRegistrationBean zuulServlet() {
		ServletRegistrationBean 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;
	}

這個zuulservlet就是類似於spring的DispatchServlet,

@Override
    public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
        try {
            init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

            // Marks this request as having passed through the "Zuul engine", as opposed to servlets
            // explicitly bound in web.xml, for which requests will not have the same data attached
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();

            try {
                preRoute();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                route();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                postRoute();
            } catch (ZuulException e) {
                error(e);
                return;
            }

        } catch (Throwable e) {
            error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }


首先是初始化servletContext,看其init方法:


void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
        zuulRunner.init(servletRequest, servletResponse);
    }
public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        RequestContext ctx = RequestContext.getCurrentContext();
        if (bufferRequests) {
            ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
        } else {
            ctx.setRequest(servletRequest);
        }

        ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
    }


實際去執行的是zuulRunner,其會給每一個請求都會創建一個requestContext,該對象會被所有的filter共享。
看其preRoute()方法,實際也是調用的zuulRunner方法,

/**
     * executes "pre" filterType  ZuulFilters
     *
     * @throws ZuulException
     */
    public void preRoute() throws ZuulException {
        FilterProcessor.getInstance().preRoute();
    }

最終交給FilterProcessor去執行,看其文檔描述 This the the core class to execute filters.


具體執行邏輯爲:

/**
     * runs all filters of the filterType sType/ Use this method within filters to run custom filters by type
     *
     * @param sType the filterType.
     * @return
     * @throws Throwable throws up an arbitrary exception
     */
    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中根據類型獲取到所有的filter,然後執行。


這一篇主要看源碼,下一篇繼續調試一遍zuul,看一下各種對象的創建和狀態。。。。。。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章