SpringCloud对Netflix Zuul的封装
Netflix zuul是提供的是作为集群服务前门的API网关服务,而SpringCloud 是以SpringBoot 为基础实现的,要在SpringCloud里面封装Netflix Zuul,则必须符合SpringBoot的理念——习惯优于配置。
@EnableZuulProxy
在SpringCloud中,几乎所有组件的使用都是以 @EnableXXX 开始的,Zuul也不例外:
@EnableCircuitBreaker
@EnableDiscoveryClient
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {
}
关键的一个地方,就是 @Import(ZuulProxyMarkerConfiguration.class) ,然而这里面除了一个类定义,并没有其他东西:
@Configuration
public class ZuulProxyMarkerConfiguration {
@Bean
public Marker zuulProxyMarkerBean() {
return new Marker();
}
class Marker {
}
}
但这个作用在什么地方,稍后再说。
ZuulProxyAutoConfiguration
SpringBoot 的一个核心就是 autoConfiguration ,默认的配置启动类在类路径中的 /META-INF/spring.factories 有定义,找到如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration
这个才是SpringBoot自动装配的启动类,查看定义:
@Configuration
@EnableConfigurationProperties({ ZuulProperties.class })
@ConditionalOnClass(ZuulServlet.class)
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)
// Make sure to get the ServerProperties from the same place as a normal web app would
@Import(ServerPropertiesAutoConfiguration.class)
public class ZuulServerAutoConfiguration {
//省略其他代码
}
其中 @ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class) 表示,如果 ZuulProxyMarkerConfiguration.Marker.class 这个类被加载了,那么 ZuulProxyAutoConfiguration 就也会被加载。再回过头去看 @EnableZuulProxy 中导入的类,正是这个 ZuulProxyMarkerConfiguration.Marker.class ,由此可见,ZuulProxyMarkerConfiguration.Marker.class 的真正作用,只是为 ZuulProxyAutoConfiguration 的加载(开启Zuul)提供一个条件。
而 @ConditionalOnClass(ZuulServlet.class) 则表明当前类路径下必须存在 ZuulServlet 类。
ZuulServlet的加载
Netflix Zuul 起作用是以ZuulServlet的启动开始的,在 SpringCloud 中,要实现对Zuul的封装,必须要实现对ZuulServlet的加载:
在 ZuulProxyAutoConfiguration 的基类 ZuulServerAutoConfiguration 中,有如下定义:
@Bean
public ZuulController zuulController() {
return new ZuulController();
}
在 ZuulServerAutoConfiguration 中加载了 ZuulController 的Bean,ZuulController 定义如下:
public class ZuulController extends ServletWrappingController {
public ZuulController() {
setServletClass(ZuulServlet.class);
setServletName("zuul");
setSupportedMethods((String[]) null); // Allow all
}
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
// We don't care about the other features of the base class, just want to
// handle the request
return super.handleRequestInternal(request, response);
}
finally {
// @see com.netflix.zuul.context.ContextLifecycleFilter.doFilter
RequestContext.getCurrentContext().unset();
}
}
}
ZuulController 在构造时就加载了 ZuulServlet。
Routes 的加载
在 SpringCloud 中使用Zuul时,会配置相应的route,这些route的加载过程:
在 ZuulServerAutoConfiguration 中有如下定义:
@Autowired
protected ZuulProperties zuulProperties;
@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);
}
@Bean
public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
mapping.setErrorController(this.errorController);
return mapping;
}
Filter的加载
在 ZuulServerAutoConfiguration 中有如下定义:
@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);
}
}
filters 是通过 @Autowired 自动注入的,所有继承ZuulFilter的类都会被组装到filters这个Map里。当 FilterLoader 把所有的 filter 都加载完毕,FilterRegistry 负责把所有的 filter 都注册到 Zuul 的 filterRegistry 里面:
@PostConstruct
public void contextInitialized() {
log.info("Starting filter initializer");
TracerFactory.initialize(tracerFactory);
CounterFactory.initialize(counterFactory);
for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {
filterRegistry.put(entry.getKey(), entry.getValue());
}
}
Request的路由
所有 request 的路由,都交由 过滤器实现,这才符合“Zuul 一切都有过滤器实现”的理念,所以,request的路由,肯定也是一个过滤器:
public class PreDecorationFilter extends ZuulFilter {
public PreDecorationFilter(RouteLocator routeLocator, String dispatcherServletPath, ZuulProperties properties,
ProxyRequestHelper proxyRequestHelper) {
this.routeLocator = routeLocator;
this.properties = properties;
this.urlPathHelper.setRemoveSemicolonContent(properties.isRemoveSemicolonContent());
this.dispatcherServletPath = dispatcherServletPath;
this.proxyRequestHelper = proxyRequestHelper;
}
@Override
public int filterOrder() {//省略部分代码}
@Override
public String filterType() {//省略部分代码}
@Overrid
public boolean shouldFilter() {//省略部分代码}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());
Route route = this.routeLocator.getMatchingRoute(requestURI);
//省略部分代码
}
//省略部分代码
}
在PreDecorationFilter类里:把HrrpServletRequest里的url path拿出来,换成了Route对象,这个对象里指定了这个Path路由到哪个服务或地址。