前言

https://github.com/Netflix/zuul

zuul 是 SpringCloud 家族老兵,使用 Java 微服务大部分都在使用 zuul 作为网关。既然他如此重要,那么我们就来分析一下。本文分析的是 zuul 2.1.5版本。

调用链路

首先,我们知道,zuul 基于 Netty,Netty 是异步网络框架。我们从调用链路出发,分析下 Zuul 的调用链路是如何串起来的。

首先看官方介绍:

既然是基于 Netty 的,我们首先看 ChannelInboundHandler,Zuul 的 ChannelInboundHandler 名为 ClientRequestReceiver,用于接收 Http 请求。

然后,ClientRequestReceiver 会将请求交给 Zuul 的过滤器链,也就是我们经常编写的 Zuul 业务逻辑。Zuul 将整个过滤器链编织成一个 ZuulFilterChainHandler,作为 ClientRequestReceiver 的下一个 Handler。最后,当过滤器处理好逻辑并得到后端返回值,将 Response 交给 ClientResponseWriter,该类会将 Response 写回给客户端。代码如下:

好,我们知道了 Zuul 基于 Netty 是如何控制调用的,那么,我们再来看看,Zuul 是如何编织过滤器链的。代码就在 com.netflix.zuul.netty.server.BaseZuulChannelInitializer#addZuulFilterChainHandler 方法中。

从上面的截图,可以看到,Zuul 先是构造了 Response Filter Chain,然后再构造 Endpoint Chain,最后构造 Request Chain,形成了一张链表,并将起封装,添加到 Netty 的 pipeline 中。

Request Chain 作为头,Response Chain 作为尾,当 Response Chain 执行完之后,则会调用 ClientResponseWriter,将返回值写回 Http Client。

这里需要提一下 Endpoint Chain 概念,首先, Zuul 作为网关,核心职责是转发请求,而 endpoint 就是目标服务器,zuul 会将请求先经过 Request Chain,然后交给 Endpoint Chain,Endpoint Chain 则会请求后端服务器,拿到返回值,然后将返回值交给 Response Chain。

那么 Zuul 是如何请求后端服务的呢?

我们看 ZuulEndPointRunner 这个类,他是 Endpoint Chain 的标准实现。filter 方法是关键。

我们看到,这里有2个红框,第一个是获取过滤器,然后,执行过滤器逻辑,得到结果。从 getEndpoint 方法看,通常我们会得到 ProxyEndpoint 过滤器,他继承自 SyncZuulFilter。分支逻辑暂时不讲。

当得到 ProxyEndpoint 后,则会执行 apply 方法。

proxyRequestToOrigin 方法,背后是 Netty 客户端请求后端服务。该方法会先连接后端服务,然后在回调中,将 Request 内容发送给后端服务。注意,发送之前,会将 此客户端的异步接收类 OriginResponseReceiver 添加到 pipeline 中,用于处理后端返回值。OriginResponseReceiver 得到返回值后,则会调用 ProxyEndpoint 的 responseFromOrigin 方法,该方法,则会将返回值交给 Response Chain。Response Chain 处理完之后,再交给 ClientResponseWriter,完成一次完整的调用。

整个调用图如下:

上图展示了一次请求的整个过程。

我们继续讨论下一个问题:Zuul 如何识别请求,并将其正确的转发的目标服务器?

假设,zuul ip port 为172.3.3.3:8080,后端服务为 172.2.2.2:8888,服务名为 userService,zuul

如何将请求转发到 172.2.2.2:8888?

首先需要定义规则,我们将服务名作为标识,通过服务名去 Eureka 找到服务地址,然后再进行调用。标识放在哪里?我们可以放在 URL 里面,比如 172.3.3.3:8080/userService/getUser 这个 url ,实际是请求的 172.2.2.2:8888/getUser 这个服务。当然,也可以将服务名放在 header 里。看自己的实现。

当 Zuul 拿到标识,则会请求 Eureka,拿到 ip 地址,构造一个 Netty 客户端,即 ProxyEndpoint 的 NettyOrigin 属性。当调用 connectToOrigin 方法后,会返回一个 Netty 的 promise,ProxyEndpoint 会将自己的调用逻辑放在回调中,即,NettyOrigin connect 成功后,ProxyEndpoint 会发送数据给后端服务器。

我们查看 com.netflix.zuul.netty.connectionpool.DefaultClientChannelManager#acquire 方法,该方法,会先调用 Server chosenServer = loadBalancer.chooseServer(key), 得到一个服务器ip port。然后得到一个 PerServerConnectionPool,

该类的 tryMakingNewConnection 方法,会构造一个 Netty 客户端。

至此,我们已经知道了 Zuul 整个的调用链路是如何实现的。

异步分析

Zuul 是如何基于 Netty 实现异步的呢?

首先入口 Handler 是 ClientRequestReceiver,当 ClientRequestReceiver 拿到请求后,会将 Request 交过过滤器链,而 Zuul 的过滤器是支持异步的。基于 RxJava 加入了 applyAsync 方法。

ProxyEndpoint 在请求后端服务时,也是异步的,ProxyEndpoint 的 apply 方法里,将自己的业务逻辑,放到了 zuul 客户端的回调里,这是基于 Netty 实现的。

流程图如下:

可以看到,Netty IO 线程,基本没有等待,从流量进来之后,要么在执行 Request chain 逻辑,要么在构建 Netty Client,没有任何等待 IO 的过程,当 Endpoint 在请求后端服务时,IO 线程是空闲的。而当后端返回时,会触发 Netty 客户端的回调,进而触发 Zuul Response 过滤器链,最后写回 ClientResponseWriter。

有个问题,异步返回时,是如何找到 ClientResponseWriter 的呢?Zuul 的实现方式是,将 ChannelHandlerContext 保存到 Session 里(_netty_server_channel_handler_context),当异步返回时,则会继续调用这个 ChannelHandlerContext 后面的 Handler 的 channelRead 方法。

Zuul 的 worker EventLoop grop 名为 Salamander-ClientToZuulWorker;

在构造请求 后端服务的 Netty 客户端时,也是使用的该 EventLoop。如下图:

可以看到,处理请求 和 处理返回值的,是同一个线程。为什么不为每个 Netty Client 设计一个 Eventloop group 呢?我的理解是代价太大了,网关会 连接成千上万个后端,如果每个 Netty Client 都配置一个 Group,那服务器估计要爆炸。

参考

开源 Zuul 2(Netflix 技术博客

https://netflixtechblog.com/open-sourcing-zuul-2-82ea476cb2b3

Zuul 2:Netflix 的异步、非阻塞系统之旅(Netflix 技术博客

https://netflixtechblog.com/zuul-2-the-netflix-journey-to-asynchronous-non-blocking-systems-45947377fb5c

Zuul 2.1.5 设计分析的更多相关文章

  1. C#小程序呢飞行棋设计分析

    C#小程序飞行棋,程序效果图 1.设计分析 这个程序界面大致分为四部分: ① 最上面游戏名字界面 ②信息提示区 ③游戏界面区 ④游戏操作提示区 2.分区设计实现 一.游戏界面显示区,由于只需要显示出图 ...

  2. Web API应用架构设计分析(2)

    在上篇随笔<Web API应用架构设计分析(1)>,我对Web API的各种应用架构进行了概括性的分析和设计,Web API 是一种应用接口框架,它能够构建HTTP服务以支撑更广泛的客户端 ...

  3. 脊柱外科病人资料管理系统的界面设计分析(2)--JOA评分记录的实现

    在上篇随笔<脊柱外科病人资料管理系统的界面设计分析>中介绍了一些常用的界面设计方面的内容,本篇继续上一篇,介绍脊柱外科病人管理系统的JOA评分记录模块的界面设计以及实现方面的内容. JOA ...

  4. Magento架构分析,Magento MVC 设计分析

    Magento架构分析,Magento MVC 设计分析 分类:Magento 标签:Magento MVC.Magento架构 669人浏览 Magento 采用类似 JAVA的架构,其扩展与稳定性 ...

  5. Netflix Zuul 了解

    Zuul 是提供动态路由,监控,弹性,安全等的边缘服务.Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门.Zuul 可以适当的对多个 Amazon Auto Scal ...

  6. netflix zuul 学习

    netflix zuul 是netflix开发的一个EDGE SERVICE. 主要是作为一个API Gateway 服务器,可以实现安全,流量控制等功能. 我看的是1.x的版本,Zuul1.x的实现 ...

  7. SpringCloud网关ZUUL集成consul

    最近一直在搞基于springcloud的微服务开发,为了不限定微服务开发语言,服务发现决定采用consul不多说上代码 pom文件 <project xmlns="http://mav ...

  8. springcloud(十):服务网关zuul

    前面的文章我们介绍了,Eureka用于服务的注册于发现,Feign支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散,Spring Cloud Config服务集群配置中心,似乎一个 ...

  9. XCOM2中敌对生物设计分析(ADVENT篇)

    最近,在制作游戏Demo--DroneAssmble的过程中,对于敌对生物的设计,参考了幽浮系列的相关设定,因此着手对幽浮2中的主要敌人进行分析. 我们知道, XCOM2中的敌对生物主要由" ...

  10. XCOM2中敌对生物设计分析(Aliens篇)

    Aliens Aliens作为游戏设定中入侵的外星人,有各式外貌及奇特的战斗方式,掌握一些高能科技或利用精神力量进行攻击 Sectoid 使用灵能战斗的外星人,并无高级版本,初级便会使用精神控制,生命 ...

随机推荐

  1. 代码发布平台jenkins中Check-out Strategy选项功能意义

    第一个选项:Use'svn update' as much as possible  这个选项能实现快速发布:Use 'svn update' whenever possible, making th ...

  2. Federated Learning001

    联邦学习--笔记001 2022.11.16周三 今天学习了联邦学习的开山之作---Communication-Efficient Learning of Deep Networks from Dec ...

  3. gin 接口开发 - 用户输入自动 TrimSpace

    最近在思考一个问题,针对用户的输入,能不能快速校验? 比方说下面的 struct,大家用过 gin 的就知道,支持指定某个字段为 required,用户如果不输入,就检验不通过. type Login ...

  4. Linux 如何删除乱码的文件

    事情是这样,服务器很多人在使用,以前的离职同事留了一大堆不知道是什么东西. 那些文件看不了,又删不掉,非常碍眼. 我搜索了挺多资料,没有一篇文章能真的解决问题(感觉都是抄来抄去的). 用 SFTP 工 ...

  5. 【WebGL系列-02】创建program上下文

    WebGL程序program对象的创建 program对象由顶点着色器对象和片元着色器对象构成,因此,创建program对象包含了两部分,一个是着色器对象的创建,一个是program对象的创建. 总体 ...

  6. 2022-02-08 IValueConverter和StringFormat

    主页 后台 stringFormat

  7. 三个编程思想:面向对象编程、面向接口编程、面向过程编程【概念解析系列_1】【C# 基础】

    〇.前言 对于 .Net 中的编程思想还是十分重要的,也是编码出高效的程序的基础! 在使用之前了解其本质,那么用起来就游刃有余.下面来简单对比下三个编程思想,看下它们都是什么,它们之间又有什么关系. ...

  8. 我通过 tensorflow 预测了博客的粉丝数

    前言: 由于最近接触了 tensorflow.js,出于试一下的心态,想通过线性回归预测一下博客的粉丝走向和数量,结果翻车了.虽然场景用错地方,但是整个实战方法用在身高体重等方面的预测还是有可行性,所 ...

  9. 如何通过cookie、session鉴权(nodejs/koa)

    http是一种无状态的协议,每一个请求都是独立的,即使同一个页面向服务器发送多次请求,服务器也无法区分是不是同一用户,所以这个时候可以借助于cookie来做身份认证,当用户登录成功,服务器为浏览器设置 ...

  10. Prompt Playground 7月开发记录

    Prompt Playground 2023年7月开发记录 上个月的时候,出于日常工作需求,做了一个简单的提示词调试工具 Prompt Playground. 这个工具的初衷是为了方便测试,所以没有做 ...