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.ZuulProxyAutoConfigurationorg.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfigurationorg.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
@Configurationpublic 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@SpringBootApplicationpublic 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去实现一些额定事件,封装了就成框架了。