一、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
会把ZuulServerAutoConfiguration
和ZuulServerAutoConfiguration
注入到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);
}
}
|
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;
}
|
1
2
3
4
|
@Bean
public ZuulController zuulController() {
return new ZuulController();
}
|
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();
}
|
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();
}
}
|
三、总结
本文配了较多的源码,主要是ZuulProxyAutoConfiguration
和ZuulServerAutoConfiguration
这两个SpringBoot自动配置类,涵盖了很多Zuul的组件,花点时间去阅读、debug会让你很快去了解去很多的工作流程和原理。虽然Zuul在国内越来越少人用了,但是源码还是值得学习的。