springcloud中zuul网关源码分析

目录

前言

一、@EnableZuulProxy

二、ZuulServerAutoConfiguration

三、ZuulProxyAutoConfiguration

总结



前言

       zuul作为网关在项目中的使用还是比较多的,本文主要从源码上分析zuul接收请求然后转发的过程。


一、@EnableZuulProxy

       首先从EnableZuulProxy注解开始分析,比较重要的是引入了ZuulProxyMarkerConfiguration配置类。

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

}

         在ZuulProxyMarkerConfiguration类中引入了Marker类。

@Configuration
public class ZuulProxyMarkerConfiguration {

	@Bean
	public Marker zuulProxyMarkerBean() {
		return new Marker();
	}
	class Marker {
	}
}

       在上述代码的实际作用为引入了ZuulProxyAutoConfiguration配置类(直接引入不好吗),ZuulProxyAutoConfiguration继承了ZuulServerAutoConfiguration类,下面主要针对两个配置类引入的组件进行分析。

@Configuration
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
		RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
		RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
		HttpClientConfiguration.class })
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {

二、ZuulServerAutoConfiguration

         在ZuulServerAutoConfiguration中引入的bean实现了接收请求,将请求转发至zuul中的一系列filter进行处理。

public class ZuulServerAutoConfiguration {
	@Bean
	public ZuulController zuulController() {
		return new ZuulController();
	}

	@Bean
	public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
		ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
		mapping.setErrorController(this.errorController);
		mapping.setCorsConfigurations(getCorsConfigurations());
		return mapping;
	}

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

       在ZuulHandlerMapping继承了AbstractUrlHandlerMapping,在DispatcherServlet中接收到请求之后会使用getHandle方法获取controller和intercepter,getHandle方法就是使用HandlerMapping,通过请求的url地址匹配对应处理器,AbstractUrlHandlerMapping的getHandlerInternal方法中调用了lookupHandler方法返回对应controller。

        ZuulHandlerMapping对lookupHandler方法进行了重写,在使用父方法查找之前调用了registerHandlers注册了一些handler。

@Override
	protected Object lookupHandler(String urlPath, HttpServletRequest request)
			throws Exception {
		if (this.errorController != null
				&& urlPath.equals(this.errorController.getErrorPath())) {
			return null;
		}
		if (isIgnoredPath(urlPath, this.routeLocator.getIgnoredPaths())) {
			return null;
		}
		RequestContext ctx = RequestContext.getCurrentContext();
		if (ctx.containsKey("forward.to")) {
			return null;
		}
		if (this.dirty) {
			synchronized (this) {
				if (this.dirty) {
					registerHandlers();
					this.dirty = false;
				}
			}
		}
		return super.lookupHandler(urlPath, request);
	}

        registerHandlers将配置的路径与ZuulController组合注册到handlerMap中,接下来的处理都是在ZuulController中。

	private void registerHandlers() {
		Collection<Route> routes = this.routeLocator.getRoutes();
		if (routes.isEmpty()) {
			this.logger.warn("No routes found from RouteLocator");
		}
		else {
			for (Route route : routes) {
				registerHandler(route.getFullPath(), this.zuul);
			}
		}
	}

            DispatcherServlet中通过适配器模式调用Controller的方法,ZuulController对应的是SimpleControllerHandlerAdapter,可以看到,在处理请求的时候直接调用handleRequest方法。

public class SimpleControllerHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof Controller);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return ((Controller) handler).handleRequest(request, response);
	}
}

            在handleRequest方法中,经过层层调用,最终调用到ZuulServlet的service方法。

	public ModelAndView handleRequest(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		try {
			// We don't care about the other features of the base class, just want to
			// handle the request
			return super.handleRequestInternal(request, response);
		}
		finally {
			// @see com.netflix.zuul.context.ContextLifecycleFilter.doFilter
			RequestContext.getCurrentContext().unset();
		}
	}
	protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
			throws Exception {

		Assert.state(this.servletInstance != null, "No Servlet instance");
		this.servletInstance.service(request, response);
		return null;
	}
	public void afterPropertiesSet() throws Exception {
		if (this.servletClass == null) {
			throw new IllegalArgumentException("'servletClass' is required");
		}
		if (this.servletName == null) {
			this.servletName = this.beanName;
		}
		this.servletInstance = ReflectionUtils.accessibleConstructor(this.servletClass).newInstance();
		this.servletInstance.init(new DelegatingServletConfig());
	}
	public ZuulController() {
		setServletClass(ZuulServlet.class);
		setServletName("zuul");
		setSupportedMethods((String[]) null); // Allow all
	}

        在service方法中,基本就是不断调用zuul中的各种filter进行处理,首先调用preFilter的方法,然后是routeFilter,最后是postFilter,如果在在执行过程中抛出ZuulException,会依次执行errorFilter和postFilter的方法。

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

        最后在runFilters方法中,会对同类型的过滤器进行排序,排序的依据是filter的filterOrder属性,所以在自定义filter时可以手动控制filter的加载顺序,从而方便处理。

   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;
    }
    public List<ZuulFilter> getFiltersByType(String filterType) {

        List<ZuulFilter> list = hashFiltersByType.get(filterType);
        if (list != null) return list;

        list = new ArrayList<ZuulFilter>();

        Collection<ZuulFilter> filters = filterRegistry.getAllFilters();
        for (Iterator<ZuulFilter> iterator = filters.iterator(); iterator.hasNext(); ) {
            ZuulFilter filter = iterator.next();
            if (filter.filterType().equals(filterType)) {
                list.add(filter);
            }
        }
        //排序
        Collections.sort(list); // sort by priority

        hashFiltersByType.putIfAbsent(filterType, list);
        return list;
    }
    public int compareTo(ZuulFilter filter) {
        return Integer.compare(this.filterOrder(), filter.filterOrder());
    }


三、ZuulProxyAutoConfiguration

       在ZuulProxyAutoConfiguration配置类中引入了一系列的filter用于对请求进行处理,本文针对其中的一部分进行说明。

public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
// pre filters
	@Bean
	@ConditionalOnMissingBean(PreDecorationFilter.class)
	public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator,
			ProxyRequestHelper proxyRequestHelper) {
		return new PreDecorationFilter(routeLocator,
				this.server.getServlet().getContextPath(), this.zuulProperties,
				proxyRequestHelper);
	}

	// route filters
	@Bean
	@ConditionalOnMissingBean(RibbonRoutingFilter.class)
	public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
			RibbonCommandFactory<?> ribbonCommandFactory) {
		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);
	}

	@Bean
	@ConditionalOnMissingBean({ SimpleHostRoutingFilter.class })
	public SimpleHostRoutingFilter simpleHostRoutingFilter2(ProxyRequestHelper helper,
			ZuulProperties zuulProperties, CloseableHttpClient httpClient) {
		return new SimpleHostRoutingFilter(helper, zuulProperties, httpClient);
	}

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

	@Bean
	public SendForwardFilter sendForwardFilter() {
		return new SendForwardFilter();
	}
}

       在DebugFilter的shouldFilter方法中调用了getParameter方法,能够将request中的param转为context的paramMap,所以如果想添加param,可以选择将filter的执行顺序放在DebugFilter的后面。

public class DebugFilter extends ZuulFilter {

	private static final DynamicBooleanProperty ROUTING_DEBUG = DynamicPropertyFactory
			.getInstance().getBooleanProperty(ZuulConstants.ZUUL_DEBUG_REQUEST, false);

	private static final DynamicStringProperty DEBUG_PARAMETER = DynamicPropertyFactory
			.getInstance().getStringProperty(ZuulConstants.ZUUL_DEBUG_PARAMETER, "debug");

	@Override
	public String filterType() {
		return PRE_TYPE;
	}

	@Override
	public int filterOrder() {
		return DEBUG_FILTER_ORDER;
	}

	@Override
	public boolean shouldFilter() {
		HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
		if ("true".equals(request.getParameter(DEBUG_PARAMETER.get()))) {
			return true;
		}
		return ROUTING_DEBUG.get();
	}
}

            在SimpleHostRoutingFilter中,context中的参数,请求头等取出,重新组合request请求。 

public class SimpleHostRoutingFilter extends ZuulFilter
		implements ApplicationListener<EnvironmentChangeEvent> {
	@Override
	public Object run() {
		RequestContext context = RequestContext.getCurrentContext();
		HttpServletRequest request = context.getRequest();
		MultiValueMap<String, String> headers = this.helper
				.buildZuulRequestHeaders(request);
		MultiValueMap<String, String> params = this.helper
				.buildZuulRequestQueryParams(request);
		String verb = getVerb(request);
		InputStream requestEntity = getRequestBody(request);
		if (getContentLength(request) < 0) {
			context.setChunkedRequestBody();
		}

		String uri = this.helper.buildZuulRequestURI(request);
		this.helper.addIgnoredHeaders();

		try {
			CloseableHttpResponse response = forward(this.httpClient, verb, uri, request,
					headers, params, requestEntity);
			setResponse(response);
		}
		catch (Exception ex) {
			throw new ZuulRuntimeException(handleException(ex));
		}
		return null;
	}
}


总结

       本文主要针对zuul处理请求的过程进行源码分析,zuul接受请求的过程基于springmvc的结构,如果在外层filter做拦截,会影响到zuul转发,本质上zuul就是一个接收请求,然后转发的过程,不过我们可以统一做鉴权和过滤等处理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值