栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

zuul网关

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

zuul网关

ZUUL
  • ZUUL初始化
  • ZUUL请求流程
  • 两个route过滤器
  • RibbonRoutingFilter

转载:
zuul初始化源码分析
zuul中的过滤器们

ZUUL初始化

在我们使用Spring Cloud Zuul通常是在启动类上添加@EnableZuulProxy注解或@EnableZuulServer。

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableZuulProxy   //这里这里
@EnableFeignClients
@Slf4j
@EnableHealth
public class ZuulServerApplication {

    public static void main(String[] args) {
       
        ApplicationContext applicationContext = SpringApplication.run(ZuulServerApplication.class, args);
        
    }
}

对比这两个注解的不同:

@EnableZuulProxy:

@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {

}

@EnableZuulServer

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@documented
@import(ZuulServerMarkerConfiguration.class)
public @interface EnableZuulServer {

}

对比结果:
1.@EnableZuulProxy是个复合注解,其引入了@EnableCircuitBreaker,也就是整合了Hystrix,而@EnableZuulServer并没有。
2.@EnableZuulProxy导入了 ZuulProxyMarkerConfiguration,而EnableZuulServer导入ZuulServerMarkerConfiguration。查看这两个不同的配置类的源码可以发现他们各自@link到ZuulProxyAutoConfiguration, ZuulServerAutoConfiguration

ZuulProxyMarkerConfiguration:


@Configuration(proxyBeanMethods = false)
public class ZuulProxyMarkerConfiguration {
	@Bean
	public Marker zuulProxyMarkerBean() {
		return new Marker();
	}
	class Marker {

	}
}

查看ZuulProxyAutoConfiguration源码可以看到类上面的@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)注解,意即:当容器中有这个(ZuulProxyMarkerConfiguration.Marker.class)bean才会触发ZuulProxyAutoConfiguration这个自动配置类,@Configuration作用和@Component差不多,就是将该类实例注入到容器

ZuulProxyAutoConfiguration:

@Configuration(proxyBeanMethods = false)
@import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
		RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
		RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
		HttpClientConfiguration.class })
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {

}

从上面的源码可以看出,ZuulProxyAutoConfiguration继承ZuulServerAutoConfiguration,可以说是对ZuulServerAutoConfiguration的一个扩展,新增了一些功能。

我们先从父类ZuulServerAutoConfiguration看看它都做了哪些事:

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({ ZuulProperties.class })  
"触发zuulProperties.class,创建了这个zuulproperties的bean"
"这个类就是把application.yaml文件中的zuul前缀的配置绑定到每个属性上"
"参考https://www.cnblogs.com/tian874540961/p/12146467.html"
@ConditionalOnClass({ ZuulServlet.class, ZuulServletFilter.class })
"@ConditionalOnClass表示只有在classpath下找到了zuulservlet.class这个类"
"才会创建ZuulServerAutoConfiguration这个bean"
@ConditionalOnBean(ZuulServerMarkerConfiguration.Marker.class)
public class ZuulServerAutoConfiguration {

	@Autowired
	protected ZuulProperties zuulProperties;  "application.yaml中前缀为zuul的属性"

	@Autowired
	protected ServerProperties server; "application.yaml中前缀为server的属性"

	@Autowired(required = false)
	private ErrorController errorController;

	private Map corsConfigurations;

	@Autowired(required = false)
	private List configurers = emptyList();

	@Bean
	public HasFeatures zuulFeature() {
		return HasFeatures.namedFeature("Zuul (Simple)",
				ZuulServerAutoConfiguration.class);
	}
	
	
	"* RouteLocator接口 中文名:路由定位器"
	"* 它有三个实现子类:"
	"* 1.CompositeRouteLocator :复合路由定位器,主要集成所有的路由定位器"
	"(如配置文件路由定位器,服务发现定位器,自定义路由定位器等)来路由定位。"
	"* 2.SimpleRouteLocator :主要加载配置文件的路由规则"
	"* 3.DiscoveryClientRouteLocator: 服务发现的路由定位器,去注册中心如Eureka,"
	"  拿到服务名称,以这样的方式/服务名称
	} 


	
	"Zuul是用来处理Http请求的,其底层是基于Servlet和一系列的Filter,而这些事如何和spring mvc集成呢?如何让spring mvc发现zuul呢"
	"ZuulServerAutoConfiguration里定义了ZuulControler和ZuulHandlerMapping,用来和spring mcv集成。其源码如下"
	@Bean
	public ZuulController zuulController() {
		return new ZuulController();
	}
	

	@Bean
	public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes,
			ZuulController zuulController) {
		ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController);
		mapping.setErrorController(this.errorController);
		mapping.setCorsConfigurations(getCorsConfigurations());
		return mapping;
	}

	protected final Map getCorsConfigurations() {
		if (this.corsConfigurations == null) {
			ZuulCorsRegistry registry = new ZuulCorsRegistry();
			this.configurers.forEach(configurer -> configurer.addCorsMappings(registry));
			this.corsConfigurations = registry.getCorsConfigurations();
		}
		return this.corsConfigurations;
	}

	@Bean
	public ApplicationListener zuulRefreshRoutesListener() {
		return new ZuulRefreshListener();
	}

	@Bean
	@ConditionalOnMissingBean(name = "zuulServlet")
	@ConditionalOnProperty(name = "zuul.use-filter", havingValue = "false",
			matchIfMissing = true)
	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;
	}

	@Bean
	@ConditionalOnMissingBean(name = "zuulServletFilter")
	@ConditionalOnProperty(name = "zuul.use-filter", havingValue = "true",
			matchIfMissing = false)
	public FilterRegistrationBean zuulServletFilter() {
		final FilterRegistrationBean filterRegistration = new FilterRegistrationBean<>();
		filterRegistration.setUrlPatterns(
				Collections.singleton(this.zuulProperties.getServletPattern()));
		filterRegistration.setFilter(new ZuulServletFilter());
		filterRegistration.setOrder(Ordered.LOWEST_PRECEDENCE);
		// The whole point of exposing this servlet is to provide a route that doesn't
		// buffer requests.
		filterRegistration.addInitParameter("buffer-requests", "false");
		return filterRegistration;
	}
	//下面是一些列zuulfilter的初始化,以下这些Filter的源码都继承了ZuulFilter
	//============================= pre filters==============================

	@Bean   "这个Filter能做什么?标记处理Servlet的类型,order是-3,检测请求是用DispatcherServlet还是zuulServlet来处理"
	public ServletDetectionFilter servletDetectionFilter() {
		return new ServletDetectionFilter();
	}

	@Bean  "这个Filter能做什么?包装请求体,order是-1"
	@ConditionalOnMissingBean
	public FormBodyWrapperFilter formBodyWrapperFilter() {
		return new FormBodyWrapperFilter();
	}

	@Bean "这个Filter能做什么?标记调试标志,order是1"
	@ConditionalOnMissingBean
	public DebugFilter debugFilter() {
		return new DebugFilter();
	}

	@Bean "这个Filter能做什么?包装HttpServletRequest请求,order是-2"
	@ConditionalOnMissingBean
	public Servlet30WrapperFilter servlet30WrapperFilter() {
		return new Servlet30WrapperFilter();
	}

	// ================================post filters===========================

	@Bean "这个Filter能做什么?处理正常返回来的响应,order是1000"
	public SendResponseFilter sendResponseFilter(ZuulProperties properties) {
		return new SendResponseFilter(zuulProperties);
	}

	@Bean "这个Filter能做什么?处理有错误的响应,order是0"
	public SendErrorFilter sendErrorFilter() {
		return new SendErrorFilter();
	}

	@Bean "这个Filter能做什么?处理forward请求转发的响应,order是500"
	public SendForwardFilter sendForwardFilter() {
		return new SendForwardFilter();
	}
	
	
	@Bean
	@ConditionalOnProperty("zuul.ribbon.eager-load.enabled")
	public ZuulRouteApplicationContextInitializer zuulRoutesApplicationContextInitiazer(
			SpringClientFactory springClientFactory) {
		return new ZuulRouteApplicationContextInitializer(springClientFactory,
				zuulProperties);
	}
	
	
	@Configuration(proxyBeanMethods = false)
	protected static class ZuulFilterConfiguration {

		@Autowired
		private Map 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(proxyBeanMethods = false)
	@ConditionalOnClass(MeterRegistry.class)
	protected static class ZuulCounterFactoryConfiguration {

		@Bean
		@ConditionalOnBean(MeterRegistry.class)
		@ConditionalOnMissingBean(CounterFactory.class)
		public CounterFactory counterFactory(MeterRegistry meterRegistry) {
			return new DefaultCounterFactory(meterRegistry);
		}

	}

	@Configuration(proxyBeanMethods = false)
	protected static class ZuulMetricsConfiguration {

		@Bean
		@ConditionalOnMissingClass("io.micrometer.core.instrument.MeterRegistry")
		@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 {

		@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
					|| 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.heartbeatMonitor.update(value)) {
				reset();
			}
		}

		private void reset() {
			this.zuulHandlerMapping.setDirty(true);
		}

	}

	private static class ZuulCorsRegistry extends CorsRegistry {

		@Override
		protected Map getCorsConfigurations() {
			return super.getCorsConfigurations();
		}

	}

}

以上是ZuulServerAutoConfiguration的主要相关内容,可以确认的是初始化了zuulProperties,各routeLocator,zuulController,zuulHandlerMapping,各zuulFilter。

ZuulProxyAutoConfiguration比ZuulServerAutoConfiguration主要新增了如下bean

1.DiscoveryClientRouteLocator
主要有两个功能,第一是从DiscoveryClient(如Eureka)发现路由信息,第二个是动态刷新路由信息

2.多了3个Filter
PreDecorationFilter: 为当前请求做一些预处理,比如:进行路由规则的匹配、在请求上下文中设置该请求的基本信息以及将路由匹配结果等一些设置信息等,这些信息将是后续过滤器进行处理的重要依据
RibbonRoutingFilter: 通过Ribbon和Hystrix来向服务实例发起请,它的order是10,类型是routeFilter。
SimpleHostRoutingFilter: 主要用来转发serviceId为空的,即直接使用httpclient来转发请求的

ZUUL请求流程

在spring mvc的DispatcherServelet的initHandlerMapping方法会将容器中所有类型为HandlerMapping.class的bean注入到spring MVC持有的handlerMappings这个List中,也就包括ZuulHandlerMapping,因为从上面的代码ZuulServrAutoConfiguration中可以看到ZuulHandlerMapping被注入到容器中。

从源码中可以看到ZuulHandlerMapping中持有RouteLocato也就是路由规则们:/zuul/api if (f.isDone()) { try { "//获取结果" f.get(); return f; } catch (Exception e) { "在获取真正的响应结果过程中如果发生了错误就会执行下面的逻辑" Throwable t = decomposeException(e); if (t instanceof HystrixBadRequestException) { "=======如果是HystrixBadRequestException,不会触发熔断降级=====" return f; } else if (t instanceof HystrixRuntimeException) { "============如果是其他异常,就会触发熔断降级?============" HystrixRuntimeException hre = (HystrixRuntimeException) t; switch (hre.getFailureType()) { case COMMAND_EXCEPTION: case TIMEOUT: return f; default: throw hre; } } else { throw Exceptions.sneakyThrow(t); } } } return f; }

queue( )方法最终会走到AbstractRibbonCommand的run( )方法中。

请关注这个类 AbstractRibbonCommand,在这个类里持RibbonCommandContext和ZuulFallbackProvider,具有ribbon和hystrix的功能。

AbstractRibbonCommand.run( ):

@Override
	protected ClientHttpResponse run() throws Exception {
		final RequestContext context = RequestContext.getCurrentContext();
		//创建请求,实现类是 RibbonApacheHttpRequest
		RQ request = createRequest();
		RS response;
		//可重试的客户端 
		boolean retryableClient = this.client instanceof AbstractLoadBalancingClient
				&& ((AbstractLoadBalancingClient)this.client).isClientRetryable((ContextAwareRequest)request);
		
		if (retryableClient) {
			"关键代码:如果是可重试的客户端,就走这里"
			response = this.client.execute(request, config);
		} else {
			//默认走这里使用负载均衡器执行
			response = this.client.executeWithLoadBalancer(request, config);
		}
		//把结果设置到RequestContext 上下文
		context.set("ribbonResponse", response);
		//响应超时,关闭response
		if (this.isResponseTimedOut()) {
			if (response != null) {
				response.close();
			}
		}
		//返回结果
		return new RibbonHttpResponse(response);
	}

后续流程可以查看
二十.SpringCloud源码剖析-Zuul使用Ribbon负载均衡-RibbonRoutingFilter

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/301172.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号