前言

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. React后台管理系统(TypeScript、Redux状态管理)环境搭建01

      搭建环境的时候,我们必须要先确保环境有node环境和npm环境,如下使用cmd命令 确保自己有了这两个环境之后我们就可以开始搭建项目,首先找一个文件夹,这个文件夹用来初始化当前环境,例如,我这里选 ...

  2. 【Shell】函数

    函数 1.语法 函数定义语法如下: [ function ] funname [()] { action; [return int;] } 说明 : 函数定义时,function 关键字可有可无. 函 ...

  3. 详解RISC v中断

    声明 本文为本人原创,未经许可严禁转载.部分图源自网络,如有侵权,联系删除. RISC-V 中断与异常 trap(陷阱)可以分为异常与中断.在 RISC v 下,中断有三种来源:software in ...

  4. 极速安装kubernetes-1.22.0(三台CentOS7服务器)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于sealos 标题敢号称极速,是因为使用了开源项目 ...

  5. DataArts Studio实践丨通过Rest Client 接口读取RESTful接口数据的能力

    本文分享自华为云社区<DataArts Studio 通过Rest Client 接口读取RESTful接口数据的能力,通过Hive-SQL存储>,作者: 张浩奇 . Rest Clien ...

  6. asp.net core之EfCore

    EF Core(Entity Framework Core)是一个轻量级.跨平台的对象关系映射(ORM)框架,用于在.NET应用程序中访问和操作数据库.它是Entity Framework的下一代版本 ...

  7. Azure Terraform(十四)Azure Key Vault 的机密管理

    一,引言 最近有网友私信我,将 Terraform 部署到 Azure 是一种将基础结构作为代码进行管理的好方法,但是如何使用 Azure Key Vault 来存储我们的 Secret ?在这篇博文 ...

  8. [ansible]wget批量调用shell脚本

    前言 相较于使用playbook,个人更习惯于编写shell脚本.如果需要多台服务器执行某一任务,可以将脚本放在某个http服务目录下,比如nginx,然后通过ansible的shell模块让服务器通 ...

  9. 论文解读(SentiX)《SentiX: A Sentiment-Aware Pre-Trained Model for Cross-Domain Sentiment Analysis》

    Note:[ wechat:Y466551 | 可加勿骚扰,付费咨询 ] 论文信息 论文标题:SentiX: A Sentiment-Aware Pre-Trained Model for Cross ...

  10. CodeForces 1367F1 Flying Sort (Easy Version)

    题意 给一个长度为\(n\)的数组,数组中的数互不相同,你可以有两种操作 将某一个数放置在数组开头 将某一个数放置在数组结尾 问最小操作多少次可以得到一个递增数列 分析 因为数组中的数很大,我们可以将 ...