Zuul在SpringCloud中起到网关的作用,可用于请求路由转发、过滤、安全检查等作用,通过@EnableZuulProxy来开启网关的配置。
一、Zuul初始化
/** * 路由网关 */ @EnableZuulProxy @EnableEurekaClient @SpringBootApplication(exclude={DataSourceAutoConfiguration.class}) @EnableSwagger2 public class RsmsZuulApplication { public static void main(String[] args) { SpringApplication.run(RsmsZuulApplication.class, args); } }
该注解用于设置Zuul服务器断点,及配置一系列的前置或后置过滤器。可以看到在该注解中引入了ZuulProxyMarkerConfiguration类。
/** * 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 Spencer Gibb * @author Dave Syer * @author Biju Kunjummen */ @EnableCircuitBreaker @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(ZuulProxyMarkerConfiguration.class) public @interface EnableZuulProxy { }
继续跟踪ZuulProxyMarkerConfiguration类,通过注释可以看到,这个类的作用主要是通过配置的Marker(Bean)来激活ZuulProxyAutoConfiguration类,
ZuulProxyAutoConfiguration是用于zuul自动装配的类,springcloud中大量使用这种装配方式,在系统启动时完成自动装配。
/** * 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 { } }
ZuulProxyAutoConfiguration配置如下
RibbonCommandFactoryConfiguration:用户处理负载均衡相关
@Configuration @Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class, RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class, RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class, HttpClientConfiguration.class }) @ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class) public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration { ... }
ZuulProxyAutoConfiguration继承ZuulServerAutoConfiguration,
ZuulServerAutoConfiguration定义了一系列的过滤器、控制器、监听器等。
其中比较重要的有zuulServlet。
1:初始化ZuulServlet
ZuulServlet类似用SpringMVC的请求转发器DispatcherServlet,当有Http请求到来时,ZuulController会交给ZuulServlet处理。ZuulServlet在根据其所管理的Filter,按执行顺序执行Filter。zuulservlet的具体执行过程后续会单独讲解。
@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; }
2:路由定位器
定义了多路定位器和简单定位器。
@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); }
SimpleRouteLocator定义路由获取的具体过程,如对前缀StripPrefix的处理、重试的配置、路由的刷新、路由规则的匹配等都是在这个类中完成。
protected Route getSimpleMatchingRoute(final String path) { if (log.isDebugEnabled()) { log.debug("Finding route for path: " + path); } // This is called for the initialization done in getRoutesMap() getRoutesMap(); if (log.isDebugEnabled()) { log.debug("servletPath=" + this.dispatcherServletPath); log.debug("zuulServletPath=" + this.zuulServletPath); log.debug("RequestUtils.isDispatcherServletRequest()=" + RequestUtils.isDispatcherServletRequest()); log.debug("RequestUtils.isZuulServletRequest()=" + RequestUtils.isZuulServletRequest()); } //预处理URL路径 String adjustedPath = adjustPath(path); //根据路径获取匹配的路由 ZuulRoute route = getZuulRoute(adjustedPath); return getRoute(route, adjustedPath); } protected Route getRoute(ZuulRoute route, String path) { if (route == null) { return null; } if (log.isDebugEnabled()) { log.debug("route matched=" + route); } String targetPath = path; String prefix = this.properties.getPrefix(); //处理前缀 if (path.startsWith(prefix) && this.properties.isStripPrefix()) { targetPath = path.substring(prefix.length()); } if (route.isStripPrefix()) { int index = route.getPath().indexOf("*") - 1; if (index > 0) { String routePrefix = route.getPath().substring(0, index); targetPath = targetPath.replaceFirst(routePrefix, ""); prefix = prefix + routePrefix; } } //是否重试 Boolean retryable = this.properties.getRetryable(); if (route.getRetryable() != null) { retryable = route.getRetryable(); } return new Route(route.getId(), targetPath, route.getLocation(), prefix, retryable, route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null, route.isStripPrefix()); }