zuul源码深度解析之一:enableZuulServer的初始化

前言

基于spring cloud分析,对应pom文件如下:

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-zuul</artifactId>
			<!--zuul-core及spring-cloud-netflix-core建议从github下载源码后导入项目引入(修改源码常用操作),如果只做学习使用且习惯借助于idle解析jar包,则可以删掉exclusions-->
			<exclusions>
				<exclusion>
					<groupId>com.netflix.zuul</groupId>
					<artifactId>zuul-core</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework.cloud</groupId>
					<artifactId>spring-cloud-netflix-core</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

源码版本:spring-cloud-start-zuul版本1.4.4.RELEASE,zuul-core版本1.3.0。建议对网关的原理及基础知识有一定了解后,再对源码进行解读。

1、zuul的初始化

使用zuul的方式非常简单,在启动类上增加@EnableZuulProxy或EnableZuulServer注解即可。

1.1、@EnableZuulServer的初始化

1.1.1、EnableZuulServer源码分析

主要引入zuulServerMakerConfiguration类。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ZuulServerMarkerConfiguration.class)
public @interface EnableZuulServer {

}

1.1.2、ZuulServerMarkerConfiguration源码分析

初始化了Marker对象,由注释可以得知@link ZuulServerAutoConfiguration

/**
 * Responsible for adding in a marker bean to trigger activation of 
 * {@link ZuulServerAutoConfiguration}
 *
 * @author Biju Kunjummen
 */

@Configuration
public class ZuulServerMarkerConfiguration {
	@Bean
	public Marker zuulServerMarkerBean() {
		return new Marker();
	}

	class Marker {
	}
}

1.1.3、ZuulServerAutoConfiguration源码分析

ZuulServerAutoConfiguration是@EnableZuulServer的核心初始化类,我们做重点分析,类的注解重点关注@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)。由此可知通过Maker对象注入spring容器,所以在application启动类加注解@EnableZuulServer,则spring容器最终会加载@zuulServerAutoConfiguration.该类加载的对象比较多,我们逐一介绍

@Configuration
@EnableConfigurationProperties({ ZuulProperties.class })
@ConditionalOnClass(ZuulServlet.class)
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)
// Make sure to get the ServerProperties from the same place as a normal web app would
@Import(ServerPropertiesAutoConfiguration.class)
public class ZuulServerAutoConfiguration {
  1. zuulProperties:加载application.properties中的配置项,常见的配置如:zuul.routes..path,zuul.routes..url等
	@Autowired
	protected ZuulProperties zuulProperties;
  1. routeLocate相关的类,网关对应路由信息的实现类,主要负责加载路由规则,是网关路由转发寻址的核心,SimpleRouteLocator加载的是配置文件的信息,注意注解标注@ConditionalOnMissingBean;CompositeRouteLocator 是组合路由,注解标注为@Primary。RouteLocator 路由规则确认会在后续章节RouteLocator路由规则加载详细介绍。
	@Bean
	@Primary
	public CompositeRouteLocator primaryRouteLocator(
			Collection<RouteLocator> routeLocators) {
		return new CompositeRouteLocator(routeLocators);
	}

	@Bean
	@ConditionalOnMissingBean(SimpleRouteLocator.class)
	public SimpleRouteLocator simpleRouteLocator() {
		return new SimpleRouteLocator(this.server.getServletPrefix(),
				this.zuulProperties);
	}
  1. ZuulController 继承ServletWrappingController,ZuulHandlerMapping 继承AbstractUrlHandlerMapping都是servlet相关组件,当发送http、https请求是spring mvc做请求分发是会查找handler(zuulHandlerMapping),在通过handlerAdapter找到合适的Handler处理器zuulController,dispatch流程会在后续Zuul请求流程中分析。
	@Bean
	public ZuulController zuulController() {
		return new ZuulController();
	}

	@Bean
	public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
		ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
		mapping.setErrorController(this.errorController);
		return mapping;
	}
  1. pre filter相关类ServletDetectionFilter ,FormBodyWrapperFilter ,DebugFilter ,Servlet30WrapperFilter
	@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();
	}
过滤器类型order执行条件描述
ServletDetectionFilterpre-3检测请求是用DispatcherServlet还是zuulServlet,该过滤器是默认过滤器中最先执行的,且总会执行(shouldFilter返回true),严格来说,若请求path,在网关有路由配置,则一定会执行。主要用来检测当前请求是通过DispatcherServlet处理运行,还是zuulServlet来处理运行。检测结果会以布尔类型保存在上下文的isDispatchServletRequest参数
@Override
	public boolean shouldFilter() {
		return true; 
	}

	@Override
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		if (!(request instanceof HttpServletRequestWrapper) 
				&& isDispatcherServletRequest(request)) {
			ctx.set(IS_DISPATCHER_SERVLET_REQUEST_KEY, true);
		} else {
			ctx.set(IS_DISPATCHER_SERVLET_REQUEST_KEY, false);
		}

		return null;
	}

zuulServlet跟DispatcherServlet的区别在于/zuul(可通过zuul.servletPath配置) 会通过zuulServlet分发,原因在于Content-Type为multipart/form的请求且是DispatcherServlet分发的请求会通过FromBodyWrapperFilter处理封装加强,而对于大文件流的请求,会影响性能。DispacherServlet分发及FromBodyWrapperFilter处理大文件的性能损耗会比较大。所以此类请求可以通过/zuul通过zuulServlet分发。

	@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;
	}
	public String getServletPattern() {
		String path = this.servletPath;
		if (!path.startsWith("/")) {
			path = "/" + path;
		}
		if (!path.contains("*")) {
			path = path.endsWith("/") ? (path + "*") : (path + "/*");
		}
		return path;
	}
过滤器类型order执行条件描述
Servlet30WrapperFilterpre-2总是执行装饰器模式,将原始的HttpServletRequest包装成Servlet30Request对象,实际等同于HttpServletRequest对象,未在代码处看到相关增强的功能
@Override
	public boolean shouldFilter() {
		return true; // TODO: only if in servlet 3.0 env
	}

	@Override
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		if (request instanceof HttpServletRequestWrapper) {
			request = (HttpServletRequest) ReflectionUtils.getField(this.requestField,
					request);
			ctx.setRequest(new Servlet30RequestWrapper(request));
		}
		else if (RequestUtils.isDispatcherServletRequest()) {
			// If it's going through the dispatcher we need to buffer the body
			ctx.setRequest(new Servlet30RequestWrapper(request));
		}
		return null;
	}
过滤器类型order执行条件描述
FromBodyWrapperFilterpre-1Content-Type为application/x-www-from-urlencode或(multipart/form-data且为DispatcherServlet)该过滤器主要作用是将符合要求的请求包装成FromBodyRequestWrapper(继承Servlet30WrapperFilter)对象
	@Override
	public boolean shouldFilter() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		String contentType = request.getContentType();
		// Don't use this filter on GET method
		if (contentType == null) {
			return false;
		}
		// Only use this filter for form data and only for multipart data in a
		// DispatcherServlet handler
		try {
			MediaType mediaType = MediaType.valueOf(contentType);
			return MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType)
					|| (isDispatcherServletRequest(request)
							&& MediaType.MULTIPART_FORM_DATA.includes(mediaType));
		}
		catch (InvalidMediaTypeException ex) {
			return false;
		}
	}

	private boolean isDispatcherServletRequest(HttpServletRequest request) {
		return request.getAttribute(
				DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null;
	}

	@Override
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		FormBodyRequestWrapper wrapper = null;
		if (request instanceof HttpServletRequestWrapper) {
			HttpServletRequest wrapped = (HttpServletRequest) ReflectionUtils
					.getField(this.requestField, request);
			wrapper = new FormBodyRequestWrapper(wrapped);
			ReflectionUtils.setField(this.requestField, request, wrapper);
			if (request instanceof ServletRequestWrapper) {
				ReflectionUtils.setField(this.servletRequestField, request, wrapper);
			}
		}
		else {
			wrapper = new FormBodyRequestWrapper(request);
			ctx.setRequest(wrapper);
		}
		if (wrapper != null) {
			ctx.getZuulRequestHeaders().put("content-type", wrapper.getContentType());
		}
		return null;
	}

FromBodyRequestWrapper的相关增强功能如下,可以看到在
getContentType,getContentLength,getContentLengthLong做了相关封装。

private class FormBodyRequestWrapper extends Servlet30RequestWrapper {

		private HttpServletRequest request;

		private volatile byte[] contentData;

		private MediaType contentType;

		private int contentLength;

		public FormBodyRequestWrapper(HttpServletRequest request) {
			super(request);
			this.request = request;
		}

		@Override
		public String getContentType() {
			if (this.contentData == null) {
				buildContentData();
			}
			return this.contentType.toString();
		}

		@Override
		public int getContentLength() {
			if (super.getContentLength() <= 0) {
				return super.getContentLength();
			}
			if (this.contentData == null) {
				buildContentData();
			}
			return this.contentLength;
		}

		public long getContentLengthLong() {
			return getContentLength();
		}

		@Override
		public ServletInputStream getInputStream() throws IOException {
			if (this.contentData == null) {
				buildContentData();
			}
			return new ServletInputStreamWrapper(this.contentData);
		}

		private synchronized void buildContentData() {
			if (this.contentData != null) {
				return;
			}
			try {
				MultiValueMap<String, Object> builder = RequestContentDataExtractor.extract(this.request);
				FormHttpOutputMessage data = new FormHttpOutputMessage();

				this.contentType = MediaType.valueOf(this.request.getContentType());
				data.getHeaders().setContentType(this.contentType);
				FormBodyWrapperFilter.this.formHttpMessageConverter.write(builder, this.contentType, data);
				// copy new content type including multipart boundary
				this.contentType = data.getHeaders().getContentType();
				byte[] input = data.getInput();
				this.contentLength = input.length;
				this.contentData = input;
			}
			catch (Exception e) {
				throw new IllegalStateException("Cannot convert form data", e);
			}
		}
	}
过滤器类型order执行条件描述
DebugFilterpre1生效方式为zuul.debug.request参数配置为true或请求参数zuul.debug.parameter设置为true在上下文设置debugRouting,debugRequest参数为true
	@Override
	public boolean shouldFilter() {
		HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
		if ("true".equals(request.getParameter(DEBUG_PARAMETER.get()))) {
			return true;
		}
		return ROUTING_DEBUG.get();
	}

	@Override
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		ctx.setDebugRouting(true);
		ctx.setDebugRequest(true);
		return null;
	}
  1. routeFilter相关类:sendFowrdFilter
过滤器类型order执行条件描述
sendFowrdFilterroute500上下文中存在forward.to值,且sendForwardFilter.ran未设置值或为false使用requestDispatcher 发送相关请求,在preDecorationFilter(@EnableZuulProxy注解引入)中会处理url为forward:开头的url地址,这部分配置forward:开头的请求会通过此过滤器转发请求。
	@Override
	public boolean shouldFilter() {
		RequestContext ctx = RequestContext.getCurrentContext();
		return ctx.containsKey(FORWARD_TO_KEY)
				&& !ctx.getBoolean(SEND_FORWARD_FILTER_RAN, false);
	}

	@Override
	public Object run() {
		try {
			RequestContext ctx = RequestContext.getCurrentContext();
			String path = (String) ctx.get(FORWARD_TO_KEY);
			RequestDispatcher dispatcher = ctx.getRequest().getRequestDispatcher(path);
			if (dispatcher != null) {
				ctx.set(SEND_FORWARD_FILTER_RAN, true);
				if (!ctx.getResponse().isCommitted()) {
					dispatcher.forward(ctx.getRequest(), ctx.getResponse());
					ctx.getResponse().flushBuffer();
				}
			}
		}
		catch (Exception ex) {
			ReflectionUtils.rethrowRuntimeException(ex);
		}
		return null;
	}
  1. post filter相关类:sendReponseFilter
过滤器类型order执行条件描述
sendReponseFilterpost1000上下文中包含请求响应相关的头信息、响应数据、响应体中的任何一个且上下文中未被设置异常,设置异常了则会给sendErrorFilter处理组装响应信息返回给客户端
	@Override
	public boolean shouldFilter() {
		RequestContext context = RequestContext.getCurrentContext();
		return context.getThrowable() == null
				&& (!context.getZuulResponseHeaders().isEmpty()
					|| context.getResponseDataStream() != null
					|| context.getResponseBody() != null);
	}

	@Override
	public Object run() {
		try {
			addResponseHeaders();
			writeResponse();
		}
		catch (Exception ex) {
			ReflectionUtils.rethrowRuntimeException(ex);
		}
		return null;
	}
  1. error filter相关类:SendErrorFilter
过滤器类型order执行条件描述
SendErrorFiltererror0上下文中包含异常信息,且sendErrorFilter.ran不为true时执行该类在1.4.4的版本中放在org.springframework.cloud.netflix.zuul.filters.post包下,此处不大规范,容易被误解成post过滤器。翻了下1.0.0的版本,SendErrorFilter类型为post类型。改成error过滤器后,结合zuulServlet中的各过滤器的执行逻辑顺序,异常处理会更加合理。error跟前面的response过滤器有个互斥条件ctx.getThrowable() ,所以最多只会执行一个
	@Override
	public boolean shouldFilter() {
		RequestContext ctx = RequestContext.getCurrentContext();
		// only forward to errorPath if it hasn't been forwarded to already
		return ctx.getThrowable() != null
				&& !ctx.getBoolean(SEND_ERROR_FILTER_RAN, false);
	}

	@Override
	public Object run() {
		try {
			RequestContext ctx = RequestContext.getCurrentContext();
			ZuulException exception = findZuulException(ctx.getThrowable());
			HttpServletRequest request = ctx.getRequest();

			request.setAttribute("javax.servlet.error.status_code", exception.nStatusCode);

			log.warn("Error during filtering", exception);
			request.setAttribute("javax.servlet.error.exception", exception);

			if (StringUtils.hasText(exception.errorCause)) {
				request.setAttribute("javax.servlet.error.message", exception.errorCause);
			}

			RequestDispatcher dispatcher = request.getRequestDispatcher(
					this.errorPath);
			if (dispatcher != null) {
				ctx.set(SEND_ERROR_FILTER_RAN, true);
				if (!ctx.getResponse().isCommitted()) {
					ctx.setResponseStatusCode(exception.nStatusCode);
					dispatcher.forward(request, ctx.getResponse());
				}
			}
		}
		catch (Exception ex) {
			ReflectionUtils.rethrowRuntimeException(ex);
		}
		return null;
	}
  1. 监控相关ZuulRefreshListener,观察者模式,监听applicaitonEvent事件。触发监听事件后,会做更新route的操作。
private static class ZuulRefreshListener
			implements ApplicationListener<ApplicationEvent> {
		

		@Autowired
		private ZuulHandlerMapping zuulHandlerMapping;

		private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();

		@Override
		public void onApplicationEvent(ApplicationEvent event) {
			if (event instanceof ContextRefreshedEvent
					|| event instanceof RefreshScopeRefreshedEvent
					|| event instanceof RoutesRefreshedEvent) {
				this.zuulHandlerMapping.setDirty(true);
			}
			else if (event instanceof HeartbeatEvent) {
				if (this.heartbeatMonitor.update(((HeartbeatEvent) event).getValue())) {
					this.zuulHandlerMapping.setDirty(true);
				}
			}
		}

	}

实际项目过程中,HeartbeatEvent会频繁的 触发,原因是引入的spring-cloud-start-zuul包中包含eureka组件。引入该组件的工程服务,DiscoveryClient会每隔30s发心跳事件。心跳代码如下

 if (clientConfig.shouldRegisterWithEureka()) {
            int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
            int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
            logger.info("Starting heartbeat executor: " + "renew interval is: " + renewalIntervalInSecs);

            // Heartbeat timer
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "heartbeat",
                            scheduler,
                            heartbeatExecutor,
                            renewalIntervalInSecs,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new HeartbeatThread()
                    ),
                    renewalIntervalInSecs, TimeUnit.SECONDS);
  1. ZuulFilterConfiguration :初始化filters,该类的主要作用是把spring容器里面的所有zuulFilter类初始化到Map filters中。执行过滤器逻辑时会根据此filters找到相关的过滤器执行。典型的责任链模式。
	@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);
		}

	}

以上是ZuulServerAutoConfiguration的主要相关内容,可以确认的是初始化了zuulProperties,各routeLocator,zuulController,zuulHandlerMapping,各zuulFilter。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值