Zuul源码解析(二)

news/2024/4/26 17:10:21/文章来源:https://blog.csdn.net/PNGYUL/article/details/130330418

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 {@Autowiredprotected ZuulProperties zuulProperties;@Autowiredprotected ServerProperties server;@Autowired(required = false)private ErrorController errorController;// ...// 这有点类似于 WebMvcConfigurerComposite// CompositeRouteLocator 就是一个集成了所有 RouteLocator 实现的一个组合类@Bean@Primarypublic 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,这个挺重要,后面会说到@Beanpublic ZuulController zuulController() {return new ZuulController();}// 新注册一个 zuul 请求的 handlerMapping,后面会详细说@Beanpublic ZuulHandlerMapping zuulHandlerMapping(RouteLocator routes) {ZuulHandlerMapping mapping = new ZuulHandlerMapping(routes, zuulController());mapping.setErrorController(this.errorController);return mapping;}// 路由刷新 Listener@Beanpublic 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@Beanpublic ServletDetectionFilter servletDetectionFilter() {return new ServletDetectionFilter();}@Beanpublic FormBodyWrapperFilter formBodyWrapperFilter() {return new FormBodyWrapperFilter();}@Beanpublic DebugFilter debugFilter() {return new DebugFilter();}@Beanpublic Servlet30WrapperFilter servlet30WrapperFilter() {return new Servlet30WrapperFilter();}// post filters@Beanpublic SendResponseFilter sendResponseFilter() {return new SendResponseFilter();}@Beanpublic SendErrorFilter sendErrorFilter() {return new SendErrorFilter();}@Beanpublic SendForwardFilter sendForwardFilter() {return new SendForwardFilter();}private static class ZuulRefreshListenerimplements ApplicationListener<ApplicationEvent> {@Autowiredprivate ZuulHandlerMapping zuulHandlerMapping;private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();@Overridepublic 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;@Autowiredprivate DiscoveryClient discovery;@Autowiredprivate 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@Beanpublic PreDecorationFilter preDecorationFilter(RouteLocator routeLocator,ProxyRequestHelper proxyRequestHelper) {return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(),this.zuulProperties, proxyRequestHelper);}// route filters@Beanpublic 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);}@Beanpublic ApplicationListener<ApplicationEvent> zuulDiscoveryRefreshRoutesListener() {return new ZuulDiscoveryRefreshListener();}private static class ZuulDiscoveryRefreshListenerimplements ApplicationListener<ApplicationEvent> {private HeartbeatMonitor monitor = new HeartbeatMonitor();@Autowiredprivate ZuulHandlerMapping zuulHandlerMapping;@Overridepublic 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.luyixian.cn/news_show_102309.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

“支付+SaaS”赋能传统产业数字化转型

易观&#xff1a;传统支付业务利润空间在政策监管和市场竞争下不断被压缩&#xff0c;多家上市支付机构逐步将业务经营重点转移切入到企业交易环节&#xff0c;为企业提供包括SaaS服务、行业解决方案、营销服务及金融科技服务等在内的企业服务收入成为新的增长点。 伴随着“十四…

Linux网络服务之yum仓库

目录 一、yum仓库简介二. ftp搭建yum源三. 搭建国内在线源四. 本地源和在线yum同时使用五. 通过缓存的方式保存所下载的软件包六 . 制作yum仓库 一、yum仓库简介 yum是一个基于RPM包&#xff08;是Red-Hat Package Manager红帽软件包管理器的缩写&#xff09;构建的软件更新机…

Java入门教程||Java 继承||Java 重写(Override)与重载(Overload)

Java 继承 继承是所有 OOP 语言和 Java 语言不可缺少的组成部分。 继承是 Java 面向对象编程技术的一块基石&#xff0c;是面向对象的三大特征之一&#xff0c;也是实现软件复用的重要手段&#xff0c;继承可以理解为一个对象从另一个对象获取属性的过程。 如果类 A 是类 B …

Automa自动化爬取文本(一)

目录 介绍 下载地址 安装教程 爬取百度热搜 介绍 Automa 是一个免费、开源的 Chrome 扩展&#xff0c;它通过目前流行的 No Code 无代码方式&#xff0c;只需要拖拽模块就实现了浏览器自动化&#xff0c;比如自动填写表格、执行重复性任务。 在工作中&#xff0c;如果我们…

learn_C_deep_5 (语句和表达式的概念、if语句的多种语法结构、C语言有没有布尔类型、“零值”的比较)

目录 语句和表达式的概念 if语句的多种语法结构 注释的便捷方法&#xff08;环境vs&#xff09; if语句执行的过程 逻辑与&& 逻辑或|| 运算关系的顺序 ​编辑 C语言有没有布尔类型 C99标准 sizeof(bool)的值为多少&#xff1f; _Bool原码 BOOL、TRUE、…

ERTEC200P-2 PROFINET设备完全开发手册(9-2)

9.2 运行AC1/AC4参考代码 修改源代码usrapp_cfg.h的宏为 #define EXAMPL_DEV_CONFIG_VERSION 44 编译后下载到评估板运行AC4示例程序 在TIA中导入GSDML-V2.35-Siemens-ERTEC200pApp44-20210623.xml。新建项目&#xff0c;添加PLC和Devkit设备。 按照如下图所示配置模块&am…

基于vfw的局域网语音聊天室系统源码论文

语音视频聊天 UDP套接字的运用 在实现语音视频聊天时,采用的是基于UDP套接字的点对点模式,而UDP面向的是无连接的数据服务,其套接字的使用如图10所示。 图10 UDP套接字的使用 视频的捕获 利用VFW接口&#xff0c;视频捕获可以分为以下几个步骤&#xff1a; 建立视频采集窗口…

stm32在SDIO模式下SD写入错误的问题

1、问题描述 使用FAT32 f_write 多次执行写操作时&#xff0c;会报FR_DISK_ERR错误&#xff0c;而且是刚开始写不报错&#xff0c;写几次后会一直报错。 设断点跟踪到HAL_SD_WriteBlocks中&#xff0c;在调用SDMMC_CmdWriteMultiBlock时&#xff0c;会报SDMMC_ERROR_TX_UNDERR…

【MYSQL】Java的JDBC编程(idea连接数据库)

1. 配置 &#xff08;1&#xff09;新建一个项目 &#xff08;2&#xff09;Build System 那里选择Maven,下一步Create &#xff08;3&#xff09;配置pom.xml文件 首先查看自己的MYSQL版本&#xff1a;进入MySQL命令窗口 我的MYSQL版本是8.0版本的. 下一步&#xff0c;…

C/C++占位符,%x和%p的区别

遇到的问题 今天遇到了一个很奇怪的问题&#xff0c;当使用malloc分配了一个堆空间后&#xff0c;分别尝试用cout和printf尝试打印该地址&#xff0c;出现了两个地址不一样的情况&#xff1a; int *pp (int*)malloc(10*sizeof(int)); *pp 1234; cout << pp << …

常用数据加密

一、加密和解密 1、 加密 数据加密的基本过程&#xff0c;就是对原来为明 的文件或数据按某种算法进行处理&#xff0c;使其成为不可读的一段代码&#xff0c;通常称为 “密文”。通过这样的途径&#xff0c;来达到保护数据不被 非法人窃取、阅读的目的。 2、解密 加密的逆…

2023年,人工智能和数据训练呈现哪些新趋势?

最近&#xff0c;多才多艺的“全能网友”ChatGPT一次次火出圈。未来人工智能将以多快的速度、发展到何种地步&#xff1f;人们已经开始坐下来认真地探讨和思考。 我们生活在这个日新月异的时代。新的一年&#xff0c;人工智能的真正能力和应用场景又发生了哪些演进&#xff1f;…

Java性能优化之序列化优化

1、Java 序列化及其缺陷 Java 提供了一种序列化机制&#xff0c;这种机制能够将一个对象序列化为二进制形式&#xff08;字节数组&#xff09;&#xff0c;用于写入磁盘或输出到网络&#xff0c;同时也能从网络或磁盘中读取字节数组&#xff0c;反序列化成对象&#xff0c;在程…

java Excel清除表格条件格式规则

© Ptw-cwl 目录 文章目录 目录Excel清除表格条件格式规则1.开始 -> 条件格式2.条件格式 -> 清除规则3.管理规则也能删除 代码报java.lang.IllegalArgumentException: Specified CF index 43 is outside the allowable range (0..42)如何解决源码 Excel清除表格条件…

GitHub 开源神器 Bark模型,让文本转语音更简单

今天跟大家分享一个文本转语音的开源模型&#xff1a;Bark Bark 是由Suno创建的基于转换器的文本到音频模型。Bark 可以生成高度逼真的多语言语音以及其他音频 - 包括音乐、背景噪音和简单的音效。该模型还可以产生非语言交流&#xff0c;如大笑、叹息和哭泣。 该项目刚开源不…

JQuery-原理示意图-- 选择器-- 选择器综合代码--jQuery 的 DOM 操作--增删改查节点--常用遍历节点方法--多选框应用--全部综合代码

目录 JQuery 基本介绍 jQuery 的原理示意图 JQuery 基本开发步骤 说明: jQuery简单示例 jQuery 对象和 DOM 对象 什么是 jQuery 对象 DOM 对象转成 jQuery 对象 应用实例 jQuery 对象转成 DOM 对象 代码演示 jQuery 选择器 jQuery 选择器介绍 jQuery 选择器的优…

根据cadence设计图学习硬件知识 day03 了解 一些芯片 和 数据手册下载的地方

1. MT53D512M32D2DS 芯片&#xff08;动态随机存取存储器&#xff09;的技术指标 1.1 16n Prefetch (预加载) (n --芯片位宽) DDR 体系 链接&#xff1a;DDR扫盲—-关于Prefetch(预取)与Burst(突发)的深入讨论_ddr prefetch_qq_25814297-npl的博客-CSDN博客 1.2 每个通…

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

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

【Springboot系列】Springboot整合Swagger3不简单

1、缘由 Swagger是一个根据代码注解生成接口文档的工具&#xff0c;减少和前端之间的沟通&#xff0c;前端同学看着文档就可以开发了&#xff0c;提升了效率&#xff0c;之前很少写swagger&#xff0c;这次自己动手写&#xff0c;还是有点麻烦&#xff0c;不怎么懂&#xff0c;…

vue3中其他的变化

1.全局API的转移 Vue 2.x 有许多全局 API 和配置。 - 例如&#xff1a;注册全局组件、注册全局指令等。 //注册全局组件 Vue.component(MyButton, {data: () > ({count: 0}),template: <button click"count">Clicked {{ count }} times.</button> …