Zuul 解决流程
一、spring-cloud-starter-zuul starter
- 咱们先查看 spring-cloud-starter-zuul starter 包下有什么,这里的重点就是 pom.xml 文件,ZuulDeprecationWarningAutoConfiguration.java
- 关上 org.springframework.cloud/spring-cloud-starter-zuul/pom.xml,能够看到是依赖了 spring-cloud-starter-netflix-zuul
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>spring-cloud-starter-zuul</artifactId>
<name>spring-cloud-starter-zuul</name>
<description>Spring Cloud Starter Zuul (deprecated, please use spring-cloud-starter-netflix-zuul)</description>
<url>https://projects.spring.io/spring-cloud</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>https://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
</project>
- 咱们查看 spring-cloud-starter-netflix-zuul 包
- 关上 pom.xml 能够看到依赖了 com.netflix.zuul,所以说 Spring Cloud Zuul 是基于 netflix 公司的 zuul 实现的,除此之外还增加了 hystrix 及 ribbon 依赖,所以 zuul 是自带这两个性能的,spring-boot-starter-web 依赖能够使利用成为 web 利用,spring-boot-starter-actuator 是监控依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix</artifactId>
<version>2.2.5.RELEASE</version>
</parent>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<name>Spring Cloud Starter Netflix Zuul</name>
<description>Spring Cloud Starter Netflix Zuul</description>
<url>https://projects.spring.io/spring-cloud</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>https://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-archaius</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.zuul</groupId>
<artifactId>zuul-core</artifactId>
</dependency>
</dependencies>
</project>
- /META-INF/spring.provides 依赖 spring-platform-netflix-core 模块及 zuul-core 模块
1 provides: spring-platform-netflix-core, zuul-core
- 当初咱们进入 spring-platform-netflix-core,看看 Spring 是怎么集成 Netflix 的一系列框架了,上面是代码框架图
- 能够看到这个 JAR 包也蕴含了 spring.factories 文件,所以 SpringBoot 我的项目启动的时候会检索此配置文件,此文件是 zuul 实现主动注册配置的要害,上面能够看到相熟的 zuul,hystrix,feign,ribbon 的主动配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration,\
org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.netflix.feign.FeignAutoConfiguration,\
org.springframework.cloud.netflix.feign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.netflix.feign.encoding.FeignContentGzipEncodingAutoConfiguration,\
org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration,\
org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration,\
org.springframework.cloud.netflix.rx.RxJavaAutoConfiguration,\
org.springframework.cloud.netflix.metrics.servo.ServoMetricsAutoConfiguration,\
org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration,\
org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration
org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.netflix.metrics.ServoEnvironmentPostProcessor
- 咱们当初关怀 Zuul 的主动配置类,从下面 spring.factories 文件能够看到和 Zuul 相干的是主动配置了两个类,下图能够看到这两个有继承关系,ZuulProxyAutoConfiguration 性能最为齐全
- ZuulServerAutoConfiguration 与 ZuulProxyAutoConfiguration
- ZuulServerAutoConfiguration 主动配置类,启动类上如果有 @EnableZuulServer 则此类失效
1. 上面代码能够看到大量应用了 @Conditional 作为条件判断,留神这个 ZuulController 这个 Bean,它是 Zuul 的申请入口,这个类实现了 Controller 了,阐明这里也应用了 Spring MVC DispatcherServlet,
2. 同时此类注册了大量的 ZuulFilter
3. 代码:
/**
* @author
*/
@Configuration // 申明是配置类
@EnableConfigurationProperties({ZuulProperties.class}) // 激活 zuul 配置
@ConditionalOnClass(ZuulServlet.class) // 条件 1 存在 ZuulServlet.class
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class) // 条件 2 存在 ZuulServerMarkerConfiguration.Marker.class bean, 即利用应用 @EnableZuulServer 注解
// Make sure to get the ServerProperties from the same place as a normal web app would
@Import(ServerPropertiesAutoConfiguration.class) // 配置 ServerProperties 实例
public class ZuulServerAutoConfiguration {
@Autowired
protected ZuulProperties zuulProperties;
@Autowired
protected ServerProperties server;
@Autowired(required = false)
private ErrorController errorController;
@Bean
public HasFeatures zuulFeature() {return HasFeatures.namedFeature("Zuul (Simple)", ZuulServerAutoConfiguration.class);
}
@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);
}
/**
* zuulController, 包装了一个 ZuulServlet 类型的 servlet, 实现对 ZuulServlet 类型的 servlet 的初始化.
*
* @return
*/
@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;
}
@Bean
public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {return new ZuulRefreshListener();
}
@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;
}
// 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();
}
// post filters
@Bean
public SendResponseFilter sendResponseFilter() {return new SendResponseFilter();
}
@Bean
public SendErrorFilter sendErrorFilter() {return new SendErrorFilter();
}
@Bean
public SendForwardFilter sendForwardFilter() {return new SendForwardFilter();
}
@Bean
@ConditionalOnProperty(value = "zuul.ribbon.eager-load.enabled", matchIfMissing = false)
public ZuulRouteApplicationContextInitializer zuulRoutesApplicationContextInitiazer(SpringClientFactory springClientFactory) {
return new ZuulRouteApplicationContextInitializer(springClientFactory,
zuulProperties);
}
@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
@ConditionalOnClass(CounterService.class)
protected static class ZuulCounterFactoryConfiguration {
@Bean
@ConditionalOnBean(CounterService.class)
public CounterFactory counterFactory(CounterService counterService) {return new DefaultCounterFactory(counterService);
}
}
@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();
}
}
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);
}
}
}
}
}
- ZuulProxyAutoConfiguration 主动配置类,启动类上如果有对应 @EnableZuulProxy 则此类失效
- 由下面此类的继承图能够发现这个类继承了 ZuulServerAutoConfiguration,所以此类领有 ZuulServerAutoConfiguration 的所有性能,并在此基础上增加了应用了服务发现作为路由寻址性能
- 代码:
/**
* @author
*/
@Configuration // 申明是配置类
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class, // 引入 RibbonCommandFactory 配置
RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
HttpClientConfiguration.class })
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class) // 条件 2 存在 ZuulProxyMarkerConfiguration.Marker.class bean, 即利用应用 @EnableZuulProxy 注解
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {@SuppressWarnings("rawtypes")
@Autowired(required = false)
private List<RibbonRequestCustomizer> requestCustomizers = Collections.emptyList();
/**
* 网关服务注册实例信息
*/
@Autowired(required = false)
private Registration registration;
/**
* 服务发现客户端
*/
@Autowired
private DiscoveryClient discovery;
/**
* serviceId 和路由的映射逻辑
*/
@Autowired
private ServiceRouteMapper serviceRouteMapper;
@Override
public HasFeatures zuulFeature() {return HasFeatures.namedFeature("Zuul (Discovery)",
ZuulProxyAutoConfiguration.class);
}
/**
* 动态和动静路由寻址: 动态从配置文件获取, 动静通过服务发现客户端实现. 后者优先级更高
* @return
*/
@Bean
@ConditionalOnMissingBean(DiscoveryClientRouteLocator.class)
public DiscoveryClientRouteLocator discoveryRouteLocator() {return new DiscoveryClientRouteLocator(this.server.getServletPrefix(),
this.discovery, this.zuulProperties, this.serviceRouteMapper, this.registration);
}
// pre filters
@Bean
public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator,
ProxyRequestHelper proxyRequestHelper) {return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(),
this.zuulProperties, proxyRequestHelper);
}
// route filters
@Bean
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 ApplicationListener<ApplicationEvent> zuulDiscoveryRefreshRoutesListener() {return new ZuulDiscoveryRefreshListener();
}
@Bean
@ConditionalOnMissingBean(ServiceRouteMapper.class)
public ServiceRouteMapper serviceRouteMapper() {return new SimpleServiceRouteMapper();
}
@Configuration
@ConditionalOnMissingClass("org.springframework.boot.actuate.endpoint.Endpoint")
protected static class NoActuatorConfiguration {
@Bean
public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {ProxyRequestHelper helper = new ProxyRequestHelper();
helper.setIgnoredHeaders(zuulProperties.getIgnoredHeaders());
helper.setTraceRequestBody(zuulProperties.isTraceRequestBody());
return helper;
}
}
/**
* 增加 Endpoint
*/
@Configuration
@ConditionalOnClass(Endpoint.class)
protected static class EndpointConfiguration {@Autowired(required = false)
private TraceRepository traces;
@ConditionalOnEnabledEndpoint("routes")
@Bean
public RoutesEndpoint routesEndpoint(RouteLocator routeLocator) {return new RoutesEndpoint(routeLocator);
}
@ConditionalOnEnabledEndpoint("routes")
@Bean
public RoutesMvcEndpoint routesMvcEndpoint(RouteLocator routeLocator,
RoutesEndpoint endpoint) {return new RoutesMvcEndpoint(endpoint, routeLocator);
}
@ConditionalOnEnabledEndpoint("filters")
@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;
}
}
private static class ZuulDiscoveryRefreshListener
implements ApplicationListener<ApplicationEvent> {private HeartbeatMonitor monitor = new HeartbeatMonitor();
@Autowired
private ZuulHandlerMapping zuulHandlerMapping;
@Override
public void onApplicationEvent(ApplicationEvent event) {if (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.monitor.update(value)) {reset();
}
}
private void reset() {this.zuulHandlerMapping.setDirty(true);
}
}
}
- ZuulServerAutoConfiguration 与 ZuulProxyAutoConfiguration 具体应用哪种模式,是别离通过 @EnableZuulServer 和 @EnableZuulProxy 注解来区别的
- 前者应用了 ZuulProperties 进行配置路由寻址;
- 后者在原来的根底上增加了应用了服务发现作为路由寻址性能, 并应用 Ribbon 做客户端的负载平衡,这个最为罕用
二、@EnableZuulProxy
- @EnableZuulProxy 注解
/**
* Sets up a Zuul server endpoint and installs some reverse proxy filters in it, so it can
* forward requests to backend servers. The backends can be registered manually through
* configuration or via DiscoveryClient.
*
* @see EnableZuulServer for how to get a Zuul server without any proxying
*
* @author
*/
@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {}
- @EnableZuulProxy 剖析
- @EnableCircuitBreaker 注解用于开启短路器性能
/**
* Annotation to enable a CircuitBreaker implementation.
* http://martinfowler.com/bliki/CircuitBreaker.html
* @author
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableCircuitBreakerImportSelector.class)
public @interface EnableCircuitBreaker {}
- @Import(ZuulProxyMarkerConfiguration.class)
- ZuulProxyMarkerConfiguration.Marker.class
@Configuration
public class ZuulProxyMarkerConfiguration {
@Bean
public Marker zuulProxyMarkerBean() {return new Marker();
}
class Marker {}}
三、应用 Consul 作为注册核心
- @EnableZuulProxy 模式下的 zuul 须要注册核心的反对,因为 eureka 曾经被抛弃了,我这里选用的是 Consul
1. 增加 Maven 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
2. 启动类上加上 @EnableDiscoveryClient 注解
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class ZuulApplication {public static void main(String[] args) {SpringApplication.run(ZuulApplication.class, args);
}
}
3. 通过以上所有步骤后,整个服务网关 Zuul 利用就能够发挥作用了
四、总结
1.Spring Cloud 对 Netflix Zuul 做了封装集成, 使得在 Spring Cloud 环境中应用 Zuul 更不便,只需增加 spring-cloud-starter-zuul maven 依赖及启动类上增加 @EnableZuulProxy 就可创立一个 zuul 利用。
2.Spring Cloud Zuul 实际上就是在 Servlet 的根底上增加了一些 ZuulFilter 去实现一些额定事件,封装了就成框架了。