Zuul processing flow
1. spring-cloud-starter-zuul starter
-
Let's first check what is in the spring-cloud-starter-zuul starter package. The focus here is the pom.xml file, ZuulDeprecationWarningAutoConfiguration.java
-
Open org.springframework.cloud/spring-cloud-starter-zuul/pom.xml, you can see that it depends on 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>
Copy code
-
Let's check the spring-cloud-starter-netflix-zuul package
-
Open pom.xml and you can see that it relies on com.netflix.zuul, so Spring Cloud Zuul is implemented based on netflix's zuul, in addition to adding hystrix and ribbon dependencies, so zuul comes with these two functions Yes, the spring-boot-starter-web dependency can make the application a web application, and the spring-boot-starter-actuator is a monitoring dependency
<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>
Copy code
- /META-INF/spring.provides depends on spring-platform-netflix-core module and zuul-core module
1 provides: spring-platform-netflix-core, zuul-core
- Now we enter spring-platform-netflix-core to see how Spring integrates a series of Netflix frameworks. Below is the code framework diagram
- You can see that this JAR package also contains the spring.factories file, so this configuration file will be retrieved when the SpringBoot project starts. This file is the key to zuul's automatic registration configuration. You can see the familiar zuul, hystrix, feign, ribbon below Auto-configuration class
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 Copy code
-
We are now concerned about Zuul s auto-configuration classes. From the spring.factories file above, we can see that there are two auto-configuration classes related to Zuul. The following figure shows that these two have an inheritance relationship. ZuulProxyAutoConfiguration has the most complete function.
-
ZuulServerAutoConfiguration and ZuulProxyAutoConfiguration
-
ZuulServerAutoConfiguration auto configuration class, if @EnableZuulServer is on the startup class, this class will take effect
1. In the code below, you can see that @Conditional is widely used as a conditional judgment. Pay attention to the ZuulController Bean, which is the request entry of Zuul. This class implements the Controller, indicating that Spring MVC DispatcherServlet is also used here. 2. At the same time, this class A large number of ZuulFilter 3 codes are registered:
/**
* @author
*/
@Configuration //The declaration is the configuration class
@EnableConfigurationProperties({ ZuulProperties.class }) //Activate zuul configuration
@ConditionalOnClass(ZuulServlet.class) //Condition 1 exists ZuulServlet.class
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class ) //Condition 2 There is a ZuulServerMarkerConfiguration.Marker.class bean, that is, the application uses the @EnableZuulServer annotation
//Make sure to get the ServerProperties from the same place as a normal web app would
@Import(ServerPropertiesAutoConfiguration.class) //Configure the ServerProperties instance
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 new SimpleRouteLocator ( the this .server.getServletPrefix (),
the this .zuulProperties);
}
/**
* zuulController, wraps a ZuulServlet type servlet, realizes the initialization of ZuulServlet type 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 );
}
}
}
}
}
Copy code
- ZuulProxyAutoConfiguration automatic configuration class, if there is a corresponding @EnableZuulProxy on the startup class, this class will take effect
- From the above inheritance diagram, we can find that this class inherits ZuulServerAutoConfiguration, so this class has all the functions of ZuulServerAutoConfiguration, and on this basis, the use of service discovery as a routing addressing function
- Code:
/**
* @author
*/
@Configuration //The declaration is the configuration class
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,//Introduce RibbonCommandFactory configuration
RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
HttpClientConfiguration.class })
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class) //Condition 2 There is a ZuulProxyMarkerConfiguration.Marker.class bean, that is, the application uses @EnableZuulProxy annotation
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
@SuppressWarnings("rawtypes")
@Autowired(required = false)
private List<RibbonRequestCustomizer> requestCustomizers = Collections.emptyList();
/**
* Gateway service registration instance information
*/
@Autowired(required = false)
private Registration registration;
/**
* Service discovery client
*/
@Autowired
private DiscoveryClient discovery;
/**
* The mapping logic of serviceId and routing
*/
@Autowired
private ServiceRouteMapper serviceRouteMapper;
@Override
public HasFeatures zuulFeature () {
return HasFeatures.namedFeature( "Zuul (Discovery)" ,
ZuulProxyAutoConfiguration.class);
}
/**
* Static and dynamic routing addressing: Static is obtained from the configuration file, and the dynamic is completed by the service discovery client. The latter has a higher priority
* @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;
}
}
/**
* Add 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 );
}
}
}
Copy code
- Which mode ZuulServerAutoConfiguration and ZuulProxyAutoConfiguration use are distinguished by @EnableZuulServer and @EnableZuulProxy annotations respectively
- The former uses ZuulProperties to configure routing addressing;
- The latter adds the use of service discovery as a routing addressing function on the original basis, and uses Ribbon for client load balancing, which is the most commonly used;
2. @EnableZuulProxy
- @EnableZuulProxy annotation
/**
* 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 {
}
Copy code
- @EnableZuulProxy analysis
- @EnableCircuitBreaker annotation is used to enable the short circuit breaker function
/**
* 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 {
}
Copy code
- @Import(ZuulProxyMarkerConfiguration.class)
- ZuulProxyMarkerConfiguration.Marker.class
@Configuration
public class ZuulProxyMarkerConfiguration {
@Bean
public Marker zuulProxyMarkerBean () {
return new Marker();
}
class Marker {
}
}
Copy code
3. use Consul as the registration center
- Zuul in @EnableZuulProxy mode needs the support of the registry, because eureka has been abandoned, I chose Consul here
1. Add Maven dependency
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> Copy code
2. Add @EnableDiscoveryClient annotation to the startup class
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class ZuulApplication {
public static void main (String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
Copy code
3. After passing all the above steps, the entire service gateway Zuul application can function
4. summary
1. Spring Cloud has encapsulated and integrated Netflix Zuul, which makes it more convenient to use Zuul in the Spring Cloud environment. You only need to add spring-cloud-starter-zuul maven dependency and add @EnableZuulProxy to the startup class to create a zuul application. 2. Spring Cloud Zuul actually adds some ZuulFilter to the Servlet to complete some additional things, and encapsulate it into a framework.