Zuul源码解析(二)

news2024/9/22 17:18:29

Zuul 的自动配置

ZuulProxyAutoConfiguration 如何触发

image-20230419183851752

如上图,在 spring.factory 中配置 ZuulProxyAutoConfiguration 自动配置了,直接点进去

image-20230419184121617

如上图所示,发现这有个条件注解,需要有 org.springframework.cloud.netflix.zuul.ZuulProxyMarkerConfiguration.Marker 这样一个bean,那这个条件是如何触发的呢?

答案时 @EnableZuulProxy 注解

image-20230419184527553

再进去 ZuulProxyMarkerConfiguration

image-20230419184654581

我们发现是通过注册一个 Marker bean 来触发 ZuulProxyAutoConfiguration,这个思想套路可以学习下。

ZuulProxyAutoConfiguration 主要自动配置了那些东西组件?

ZuulProxyAutoConfiguration 继承了 ZuulServerAutoConfiguration。这两个类中有分别有所负责自动配置的内容

ZuulServerAutoConfiguration

主要配置了 CompositeRouteLocator,SimpleRouteLocator,ZuulController,ZuulHandlerMapping 以及一些默认 Filter 等 Zuul 服务组件。

@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 {

	@Autowired
	protected ZuulProperties zuulProperties;

	@Autowired
	protected ServerProperties server;

	@Autowired(required = false)
	private ErrorController errorController;
    // ...
    // 这有点类似于 WebMvcConfigurerComposite
    // CompositeRouteLocator 就是一个集成了所有 RouteLocator 实现的一个组合类
	@Bean
	@Primary
	public CompositeRouteLocator primaryRouteLocator(
			Collection<RouteLocator> routeLocators) {
		return new CompositeRouteLocator(routeLocators);
	}

    // ConditionalOnMissingBean 表示我们可自定义
	@Bean
	@ConditionalOnMissingBean(SimpleRouteLocator.class)
	public SimpleRouteLocator simpleRouteLocator() {
		return new SimpleRouteLocator(this.server.getServletPrefix(),
				this.zuulProperties);
	}

   	// a handler,这个挺重要,后面会说到
	@Bean
	public ZuulController zuulController() {
		return new ZuulController();
	}

    // 新注册一个 zuul 请求的 handlerMapping,后面会详细说
	@Bean
	public ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {
		ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());
		mapping.setErrorController(this.errorController);
		return mapping;
	}

    // 路由刷新 Listener
	@Bean
	public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
		return new ZuulRefreshListener();
	}

	@Bean
	@ConditionalOnMissingBean(name = "zuulServlet")
	public ServletRegistrationBean zuulServlet() {
		ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(),
				this.zuulProperties.getServletPattern());
		servlet.addInitParameter("buffer-requests", "false");
		return servlet;
	}

  	// 下面时注册了前面介绍的几个默认的 Filter
	// pre filters
	@Bean
	public ServletDetectionFilter servletDetectionFilter() {
		return new ServletDetectionFilter();
	}

	@Bean
	public FormBodyWrapperFilter formBodyWrapperFilter() {
		return new FormBodyWrapperFilter();
	}

	@Bean
	public DebugFilter debugFilter() {
		return new DebugFilter();
	}

	@Bean
	public Servlet30WrapperFilter servlet30WrapperFilter() {
		return new Servlet30WrapperFilter();
	}

	// post filters
	@Bean
	public SendResponseFilter sendResponseFilter() {
		return new SendResponseFilter();
	}

	@Bean
	public SendErrorFilter sendErrorFilter() {
		return new SendErrorFilter();
	}

	@Bean
	public SendForwardFilter sendForwardFilter() {
		return new SendForwardFilter();
	}

	private static class ZuulRefreshListener
			implements ApplicationListener<ApplicationEvent> {

		@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) {
				this.zuulHandlerMapping.setDirty(true);
			}
			else if (event instanceof HeartbeatEvent) {
				if (this.heartbeatMonitor.update(((HeartbeatEvent) event).getValue())) {
					this.zuulHandlerMapping.setDirty(true);
				}
			}
		}

	}
}

ZuulProxyAutoConfiguration

主要配置了 DiscoveryClientRouteLocator,pre filters,route filters,ZuulDiscoveryRefreshListener 路由监听刷新等 Zuul 代理相关的组件

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

	@SuppressWarnings("rawtypes")
	@Autowired(required = false)
	private List<RibbonRequestCustomizer> requestCustomizers = Collections.emptyList();

	@Autowired(required = false)
	private Registration registration;

	@Autowired
	private DiscoveryClient discovery;

	@Autowired
	private ServiceRouteMapper serviceRouteMapper;

	@Bean
	@ConditionalOnMissingBean(DiscoveryClientRouteLocator.class)
	public DiscoveryClientRouteLocator discoveryRouteLocator() {
		return new DiscoveryClientRouteLocator(this.server.getServletPrefix(),
				this.discovery, this.zuulProperties, this.serviceRouteMapper, this.registration);
	}

	// pre filters
	@Bean
	public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator,
			ProxyRequestHelper proxyRequestHelper) {
		return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(),
				this.zuulProperties, proxyRequestHelper);
	}

	// route filters
	@Bean
	public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
			RibbonCommandFactory<?> ribbonCommandFactory) {
		RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory,
				this.requestCustomizers);
		return filter;
	}

	@Bean
	@ConditionalOnMissingBean({SimpleHostRoutingFilter.class, CloseableHttpClient.class})
	public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper,
			ZuulProperties zuulProperties,
			ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
			ApacheHttpClientFactory httpClientFactory) {
		return new SimpleHostRoutingFilter(helper, zuulProperties,
				connectionManagerFactory, httpClientFactory);
	}

	@Bean
	@ConditionalOnMissingBean({SimpleHostRoutingFilter.class})
	public SimpleHostRoutingFilter simpleHostRoutingFilter2(ProxyRequestHelper helper,
														   ZuulProperties zuulProperties,
														   CloseableHttpClient httpClient) {
		return new SimpleHostRoutingFilter(helper, zuulProperties,
				httpClient);
	}

	@Bean
	public ApplicationListener<ApplicationEvent> zuulDiscoveryRefreshRoutesListener() {
		return new ZuulDiscoveryRefreshListener();
	}

	private static class ZuulDiscoveryRefreshListener
			implements ApplicationListener<ApplicationEvent> {

		private HeartbeatMonitor monitor = new HeartbeatMonitor();

		@Autowired
		private ZuulHandlerMapping zuulHandlerMapping;

		@Override
		public void onApplicationEvent(ApplicationEvent event) {
			if (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.monitor.update(value)) {
				reset();
			}
		}

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

	}

Zuul 又是怎么和 MVC 中的 DisPatcherServlet 联系起来的

ps:这部分的理解需要读者简单了解 Spring MVC 原理和组件

在文章的最开头部分,我们看到打印的错误日志是从 DispatcherServlet 进来的。当时由于我对 Zuul 的实现原理不了解,以为他是独立于普通请求的 DispatcherServlet。据我了解 DispatcherServlet 的默认匹配路径是 /,而 ZuulServlet 的默认匹配路径是 /zuul/**,所以我们项目中,一般的 https://网关域名/usercenter/user/detail/1 等的路径都是走的 DispatcherServlet 。

我们知道,ZuulServlet#service() 方法逻辑程序才是 Zuul 核心流程代码,那么它又是如何在从 DispatcherServlet.service() 方法中进入 ZuulServlet 的呢?

因为 Zuul 自动配置中配置了一个 ZuulHandlerMapping。

接着我带着你们去一探究竟~

首先入口还是回到大家熟悉的 DispatcherServlet#doDispatch()

getHandler() 这个方法很重要,这个方法返回的 mappedhandler 中的 hanlder 是 Zuul 自动配置的 ZuulController 的实例。

那么现在你也许有两个疑问:

  1. getHandler() 是怎么根据请i去 urlPath 就匹配到了 ZuulController
  2. ZuulController 又是如何与 ZuulServlet 联系在一起的,是如何进入ZuulServlet 的 service() 方法

getHandler() 是怎么根据请求 urlPath 就匹配到了 ZuulController

进入 getHandler() 方法,发现它是遍历了所有的 HandlerMapping,这其中就包括前面讲 Zuul 自动配置的 ZuulHandlermapping。

根据请求 urlPath,实际上只有 ZuulHandlermapping#getHandler() 方法会返回 handler。

接着进入 ZuulHandlermapping#getHandler(),看他是如何匹配的。

如下图,它继续调用了 ZuulHandlermapping 父类的 AbstractUrlhandlerMapping 的 getHandlerInternal()

如下图,AbstractUrlhandlerMapping 的 getHandlerInternal() 中在调用了子类 ZuulHandlermapping 的 lookupHandler。

在这个方法中,主要做了两件事:

  1. 如果 dirty 为 true,则会注册 handler 到一个 map 中。(一般容器启动时或者新部署应用服务时,dirty 会被改为 true,目的就是实时刷新 这个 map,前面在讲 RefreshableRouteLocator 时有介绍过)
  2. 紧接着再在这个 map 中查找 handler

不理解不要紧,先往下看,我详细说下这两个步骤。

我们进入 registerHandlers() 这个方法,看他如何注册 handler

上图中,首先通过路由定位器调用 getRoutes() 方法获取所有的路由(这个方法前面已经介绍过了)。然后遍历每个 route,每个 routefullPathkey,ZuulController 为 value,put 到一个 map 中去。(比如 /usercenter/** map to ZuulController,/ordercenter/** map to ZuulController,这里的ZuulControler 都是同一个。)

再来看看第二个问题,如何在 map 中匹配到 handler 的,如下图:

比如 urlPath=/usercenter/user/detail/1,那么就会跟 handlerMap 中的 key 为 /usercenter/** 匹配上,然后就返回 handlerMap 的值 ZuulController 了

ZuulController 又是如何于 ZuulServlet 联系在一起的,或者说是如何进入ZuulServlet 的 service() 方法

再回到 DisPatcherServlet,getHandlerAdapter() 获取到 SimpleControllerHandlerAdapter,紧接着调用SimpleControllerHandlerAdapter 的 handle() 方法。如下图:

点击进入 handler() 方法,如下图。

再点击上图中的 handleRequest() 方法,则进入了 ``ZuulController#handleRequest() 中,ZuulController#handleRequest() 没做任何处理,直接调用了父类 ServletWrappingController#handleRequestInternal()` 方法,如下图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mr8ZYYgi-1682254631363)(null)]

这里可能又有个疑问了,上图中的 servletInstance 是个什么东西,是 ZuulServlet?没错,就是它。

那怎么确定就是 ZuulServlet 呢,如下图,可以看到,ZuulController 在初始化时是指定了 ZuulServlet.class

那么,全文到这里就结束了。

由于全文篇幅太长,我把 Zuul 的源码解析分成了两篇文章,感兴趣可前往 Zuul源码解析(一)
如果你还有其他疑问,可以联系我,一起学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/453100.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kubernetes 核心对象

Kubernetes 中的所有内容都被抽象为“资源”&#xff0c;如 Pod、Service、Node 等都是资源。“对象”就是“资源”的实例&#xff0c;是持久化的实体。Kubernetes 使用这些实体去表示整个集群的状态。它们主要描述了如下信息&#xff1a; 哪些容器化应用正在运行&#xff08;…

ObjectARX中的坐标系以及坐标转换

1 AutoCAD中的坐标系种类 WCS World Coordinate System. The “reference” coordinate system. All other coordinate systems are defined relative to the WCS, which never changes. Values measured relative to the WCS are stable across changes to other coordinate s…

Nginx之正则表达式、location匹配简介以及rewrite重写

引言 在Nginx已经成为很多公司作为前端反向代理服务器的首选&#xff0c;在实际工作中往往会遇到很多跳转(重写URL)的需求。比如更换域名后需要保持旧的域名能跳转到新的域名上、某网页发生改变需要跳转到新的页面、网站防盗链等等需求。如果在后端使用的Apache服务器&#xff…

力扣---LeetCode203. 移除链表元素

文章目录 前言203. 移除链表元素链接方法一&#xff1a;直接删除1.1 注意&#xff1a;1.2 各种情况的流程图&#xff1a;方法二&#xff1a;遍历将非val的节点尾插2.1 流程图&#xff1a;2.2 注意&#xff1a; 总结 前言 你坚持的东西 总有一天会反过来拥抱你 本章的内容是力扣…

智安网络|数据安全问题频发,首推云墙·网站综合防御系统

近期&#xff0c;企业数据安全问题频发&#xff0c;数据安全依旧是企业目前面临的重大风险之一。作为网络安全公司&#xff0c;智安网络以专业的技术团队和丰富的行业经验&#xff0c;为企业和个人提供安全可靠的数据保护服务 案例一&#xff1a; 大学生学习软件超星学习通(下称…

JUC高级十二-ReentrantLock、ReentrantReadWriteLock、StampedLock

无锁→独占锁→读写锁→邮戳锁 1. 关于锁的大厂面试题 你知道Java里面有哪些锁?你说你用过读写锁&#xff0c;锁饥饿问题是什么&#xff1f;有没有比读写锁更快的锁&#xff1f;StampedLock知道吗?(邮戳锁/票据锁)ReentrantReadWriteLock有锁降级机制策略你知道吗&#xff1…

Spring的创建与Bean对象的存取

文章目录&#xff1a;一.Spring项目的创建1.先创建maven项目 2.添加国内源 3.添加spring依赖 4.创建spring配置文件 5.创建启动类 二.Bean对象的创建和读取1.Bean对象的创建与存储方式&#xff08;1&#xff09;类注解 &#xff08;2&#xff09;方法注解 &#xff08;3&#x…

Java中的容器大杂烩-集合

Java中的容器大杂烩-集合 一、 集合引入二、集合框架体系三 、Collection 接口四 、List集合4.1 ArrayList类4.2 LinkedList类4.3 Vector类4.4 ArrayList 、 LinkedList 和 Vector区别 五 、Set集合5.1 HashSet类5.2 TreeSet5.3 LinkedHashSet类 六、List和Set区别七 、Map集合…

制造业巨头遭黑客勒索400万美元,企业如何防范勒索病毒攻击?

4月7日&#xff0c;台湾电脑制造商微星&#xff08;简称MSI&#xff09;发布公开声明&#xff0c;证实其部分网络信息系统遭受了勒索病毒攻击。一个名为“Money Message”的新网络黑客团伙称其从微星的网络系统中窃取了1.5TB的数据&#xff0c;其中包括微星数据库的屏幕截图、源…

YAML /Excel /CSV?自动化测试测试数据管理应用,测试老鸟总结...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自动化测试无论是…

webhub123 前端技术社区和技术交流学习网站导航

整理了学习前端技术可以参考学习和技术交流的一些网站集合&#xff0c;全部收录到 webhub123 前端技术社区和技术交流学习网站导航http://​www.webhub123.com/#/home/detail?projectHashid30929575&ownerUserid22053727 整理后的效果如下&#xff0c;我们已经按照不同类…

React Props

state 和 props 主要的区别在于 props 是不可变的&#xff0c;而 state 可以根据与用户交互来改变。 所以&#xff0c;有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。 props 使用 Demo.js &#xff1a; import React from reactfunct…

智能学习 | MATLAB实现ACO-BP多变量时间序列预测(蚁群算法优化BP神经网络)

智能学习 | MATLAB实现ACO-BP多变量时间序列预测(蚁群算法优化BP神经网络) 目录 智能学习 | MATLAB实现ACO-BP多变量时间序列预测(蚁群算法优化BP神经网络)预测效果基本介绍程序设计参考资料预测效果 基本介绍 MATLAB实现ACO-BP多变量时间序列预测(蚁群算法优化BP神经网络…

数据集合注入

集合注入 前面我们已经能完成引入数据类型和简单数据类型的注入&#xff0c;但是还有一种数据类型集合&#xff0c;集合中既可 以装简单数据类型也可以装引用数据类型&#xff0c;对于集合&#xff0c;在Spring中该如何注入呢? 先来回顾下&#xff0c;常见的集合类型有哪些…

Vue电商项目--应用开发详解

vue-cli脚手架初始化项目 首先&#xff0c;页面上新建一个文件夹。然后打开命令端口 vue create app 选择Default ([Vue 2] babel, eslint) 然后把项目拖拽到vscode中。项目目录看一下 脚手架项目的目录 node_modules:放置项目依赖的地方 public:一般放置一些共用的静态资源&a…

数据采集方式有哪些,都有什么特点?

随着中国社会的进一步发展&#xff0c;各行各业都得到了一定程度的进步。进入21世纪以来&#xff0c;大数据、人工智能等行业的飞速发展&#xff0c;极大的带动全社会进步。但是&#xff0c;在一些传统行业内部&#xff0c;还存在这落后的东西&#xff0c;例如数据采集还是沿用…

【机器学习】P24 随机森林算法(1) 实现 “鸢尾花” 预测

随机森林算法 Random Forest Algorithm 随机森林算法随机森林算法实现分类鸢尾花 随机森林算法 随机森林&#xff08;Random Forest&#xff09;算法 是一种 集成学习&#xff08;Ensemble Learning&#xff09;方法&#xff0c;它由多个决策树组成&#xff0c;是一种分类、回…

OS实战笔记(8)-- 设置基本OS基本工作环境

本笔记会搭建OS实战所需的虚拟机环境&#xff0c;主要是创建好虚拟机&#xff0c;设置虚拟机启动硬盘&#xff0c;在启动盘上安装Grub。 由于本专题是个人在业余时间除了Unity学习之外做的&#xff0c;没有时间和精力去解答具体的问题。本笔记中的实验个人在做的过程中除了遇到…

17.集合

集合 集合类是Java数据结构的实现。Java的集合类是java.util包中的重要内容&#xff0c;它允许以各种方式将元素分组&#xff0c;并定义了各种使这些元素更容易操作的方法。Java集合类是Java将一些基本的和使用频率极高的基础类进行封装和增强后再以一个类的形式提供。集合类是…

整理现有的wiki私服项目

文章目录 核心功能现有项目wikijsBookStackmediawikiTiddlyWikigollumdokuwikixwiki 总结参考 核心功能 查找编辑 在线/离线内链【核心】代码高亮图表、表达式生成多媒体&#xff08;图片、音频、视频&#xff09;管理 协作&#xff08;用户管理模式/github模式&#xff09; 修…