Featured image of post Spring Cloud微服务网关Zuul的注解@EnableZuulProxy或@EnableZuulServer做了什么事情

Spring Cloud微服务网关Zuul的注解@EnableZuulProxy或@EnableZuulServer做了什么事情

一、Zuul的工作原理

Zuul 1.x的版本是由Servlet以及一系列的Filter组成的,各个组件之间协同合作完成功能,且易于扩展。参看官方的架构图我画了张图:

Zuul声明周期:

HTTP Request -> DispatcherServlet -> ZuulHandlerMapping -> ZuulController -> ZuulServlet -> ZuulRunner -> FilterProcessor -> ZuulFilter -> HTTP Response

1、ZuulServlet通过RequestContext上下文管理着Zuul众多的Filter组成的核心组件。

2、当有请求进入Zuul时,由DispatcherServlet 进行分发交给ZuulHandlerMapping处理初始化得到路由定位器RouteLocator,为后续的请求分发做准备,同时也有基于事件从服务中心拉去服务列表(ZuulRefreshListener)。

3、ZuulController继承了ServletWrappingController重写了handleRequest方法将ZuulServlet引入声明周期,之后所有的请求都会经过ZuulServlet

4、在第一次调用的时候会初始化ZuulRunner,后面就会按照过滤链的顺序执行。ZuulRunner会将请求和相应初始化为RequestContext,封装成FilterProcessor转化为调用preRoute()route()postRoute()error()。所以在Zuul中获取请求都是通过RequestContext.getCurrentContext()先获取当前上下文,再通过上下文获取当前的请求。

二、@EnableZuulPoxy和@EnableZuulServer注解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {
}



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

在网关的启动类上,添加注解@EnableZuulPoxy后就可以启动Zuul网关的功能。从上面的源码可以看到除了@EnableCircuitBreaker启动了熔断器外还引入了一个自动配置的类ZuulProxyMarkerConfiguration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/**
 * Responsible for adding in a marker bean to trigger activation of 
 * {@link ZuulProxyAutoConfiguration}
 *
 * @author Biju Kunjummen
 */
@Configuration
public class ZuulProxyMarkerConfiguration {
	@Bean
	public Marker zuulProxyMarkerBean() {
		return new Marker();
	}
	class Marker {
	}
}

ZuulProxyMarkerConfiguration的源码十分简单啊!看不出什么来,不过注释里面说了添加Marker bean是为了触发ZuulProxyAutoConfiguration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Configuration
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
		RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
		RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
		HttpClientConfiguration.class })
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
	@Autowired(required = false)
	private List<RibbonRequestCustomizer> requestCustomizers = Collections.emptyList();
	@Autowired(required = false)
	private Registration registration;
	@Autowired
	private DiscoveryClient discovery;
	@Autowired
	private ServiceRouteMapper serviceRouteMapper;
	...
}

很明显了@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)条件注入。在ZuulProxyAutoConfiguration里面注入了很多的Bean

2.1 ZuulProxyAutoConfiguration

ZuulProxyAutoConfiguration继承了ZuulServerAutoConfiguration,拥有其全部功能并新增一下功能:

  • 引入HTTP客户端的配置:支持Apache HttpClient 和 OkHttp:通过@import注解引入配置,因为Zuul也是要发起HTTP请后网关后的服务,所以是需要HTTP客户端。
1
2
3
4
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
		RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
		RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
		HttpClientConfiguration.class })
  • 初始化服务注册、发现监听器:在发生服务注册到注册中心时触发路由刷新,DiscoveryClientRouteLocator完成路由新刷新功能。
1
2
3
4
5
6
@Bean
@ConditionalOnMissingBean(DiscoveryClientRouteLocator.class)
public DiscoveryClientRouteLocator discoveryRouteLocator() {
	return new DiscoveryClientRouteLocator(this.server.getServlet().getServletPrefix(), this.discovery, this.zuulProperties,
			this.serviceRouteMapper, this.registration);
}
  • 初始化服务列表监听器

  • 初始化Zuul自定义Endpoint:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Configuration
@ConditionalOnClass(Health.class)
protected static class EndpointConfiguration {
	@Autowired(required = false)
	private HttpTraceRepository traces;
	@Bean
	@ConditionalOnEnabledEndpoint
	public RoutesEndpoint routesEndpoint(RouteLocator routeLocator) {
		return new RoutesEndpoint(routeLocator);
	}
	@ConditionalOnEnabledEndpoint
	@Bean
	public FiltersEndpoint filtersEndpoint() {
		FilterRegistry filterRegistry = FilterRegistry.instance();
		return new FiltersEndpoint(filterRegistry);
	}
	@Bean
	public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
		TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
		if (this.traces != null) {
			helper.setTraces(this.traces);
		}
		helper.setIgnoredHeaders(zuulProperties.getIgnoredHeaders());
		helper.setTraceRequestBody(zuulProperties.isTraceRequestBody());
		return helper;
	}
}
  • 初始化一些内置的Filter(ZuulServerAutoConfiguration没有的Filter):根据HttpClient或OkHttp制定负载均衡的过滤器。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// pre filters
@Bean
@ConditionalOnMissingBean(PreDecorationFilter.class)
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelpe
	return new PreDecorationFilter(routeLocator, this.server.getServlet().getServletPrefix(), this.zuulPropert
			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);
}

2.2 ZuulServerAutoConfiguration

同时可以看到ZuulProxyAutoConfiguration是继承了ZuulServerAutoConfiguration。同样@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)条件注入。所以说@EnableZuulProxy会把ZuulServerAutoConfigurationZuulServerAutoConfiguration注入到Spring容器中。

很大一部分的组件实在ZuulServerAutoConfiguration中注入到Spring容器的:

1
2
3
4
5
6
7
8
9
@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
// FIXME @Import(ServerPropertiesAutoConfiguration.class)
public class ZuulServerAutoConfiguration {
	...
}
  • 初始化配置加载器:读取配置文件中的配置
1
2
@Autowired
protected ZuulProperties zuulProperties;
  • 初始化路由加载器:这里的路由加载器都是加载本地配置文件中配置的路由信息。如果是想要自己实现路由加载器,例如像DiscoveryClientRouteLocator就是注册中心发生注册事件时,加载新的服务路由的加载器。如果是基于数据库存储做动态路由信息,也要自己实现一个路由加载器。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Bean
@Primary
public CompositeRouteLocator primaryRouteLocator(
		Collection<RouteLocator> routeLocators) {
    // 这里整合所有的路由加载器,像要刷新路由时,会遍历Spring容器内所有的路由加载器一个个去刷新。
	return new CompositeRouteLocator(routeLocators);
}
@Bean
@ConditionalOnMissingBean(SimpleRouteLocator.class)
public SimpleRouteLocator simpleRouteLocator() {
    // 这是最基本的路由加载器,无论是DiscoveryClientRouteLocator还是自定义的都需要继承SimpleRouteLocator
    // 所有的路由信息最终都会加载出来,被SimpleRouteLocator管理着
	return new SimpleRouteLocator(this.server.getServlet().getServletPrefix(),
			this.zuulProperties);
}
  • 初始化路由映射器:它会根据请求路径,通过路由加载器匹配可以访问的服务
1
2
3
4
5
6
@Bean
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
	ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
	mapping.setErrorController(this.errorController);
	return mapping;
}
  • 初始化路由刷新监听器:这个是很核心的配置,因为这里监听着各种会触发路由刷新的事件。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Bean
public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
	return new ZuulRefreshListener();
}

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
				|| event instanceof InstanceRegisteredEvent) {
			reset();
		}
		else if (event instanceof ParentHeartbeatEvent) {
			ParentHeartbeatEvent e = (ParentHeartbeatEvent) event;
			resetIfNeeded(e.getValue());
		}
		else if (event instanceof HeartbeatEvent) {
			HeartbeatEvent e = (HeartbeatEvent) event;
			resetIfNeeded(e.getValue());
		}
	}
	private void resetIfNeeded(Object value) {
		if (this.heartbeatMonitor.update(value)) {
			reset();
		}
	}
	private void reset() {
		this.zuulHandlerMapping.setDirty(true);
	}
}
  • 初始化ZuulServlet加载器
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Bean
@ConditionalOnMissingBean(name = "zuulServlet")
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;
}
  • 初始化ZuulController
1
2
3
4
@Bean
public ZuulController zuulController() {
	return new ZuulController();
}
  • 初始化Filter执行解析器
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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);
	}
}@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);
	}
}
  • 初始化一些内置的Filter:基本的过滤器,也是经常会在很多Zuul过滤链组成图中经常看到。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@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();
}
// post filters
@Bean
public SendResponseFilter sendResponseFilter(ZuulProperties properties) {
	return new SendResponseFilter(zuulProperties);
}
@Bean
public SendErrorFilter sendErrorFilter() {
	return new SendErrorFilter();
}
@Bean
public SendForwardFilter sendForwardFilter() {
	return new SendForwardFilter();
}
  • 初始化Metrix监控
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Configuration
protected static class ZuulMetricsConfiguration {
	@Bean
	@ConditionalOnMissingBean(CounterFactory.class)
	public CounterFactory counterFactory() {
		return new EmptyCounterFactory();
	}
	@ConditionalOnMissingBean(TracerFactory.class)
	@Bean
	public TracerFactory tracerFactory() {
		return new EmptyTracerFactory();
	}
}

三、总结

本文配了较多的源码,主要是ZuulProxyAutoConfigurationZuulServerAutoConfiguration这两个SpringBoot自动配置类,涵盖了很多Zuul的组件,花点时间去阅读、debug会让你很快去了解去很多的工作流程和原理。虽然Zuul在国内越来越少人用了,但是源码还是值得学习的。