共计 16248 个字符,预计需要花费 41 分钟才能阅读完成。
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 去实现一些额定事件,封装了就成框架了。
正文完