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,看一下各种对象的创建和状态。。。。。。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值