SpringCloud对Netflix Zuul的封装

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 都注册到 ZuulfilterRegistry 里面:

@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路由到哪个服务或地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值