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路由到哪個服務或地址。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章