前言

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. https 原理分析进阶-模拟https通信过程

    大家好,我是蓝胖子,之前出过一篇https的原理分析 ,完整的介绍了https概念以及通信过程,今天我们就来比较完整的模拟实现https通信的过程,通过这篇文章,你能了解到https核心的概念以及原理 ...

  2. 【原创】xenomai内核解析-xenomai实时线程创建流程

    版权声明:本文为本文为博主原创文章,未经同意,禁止转载.如有错误,欢迎指正,博客地址:https://www.cnblogs.com/wsg1100/ 目录 问题概述 1 libCobalt中调用非实 ...

  3. 跨越HTTP无状态边界:Cookie与Session在Django中的实战应用

    本文深入探索了Django中的Cookie和Session,解析了如何应对HTTP协议的无状态性问题,说明其基础概念,分析工作原理,并讨论何时应选择使用Cookie或Session.文章进阶部分,提出 ...

  4. 「学习笔记」FHQ-treap

    FHQ-treap,即无旋 treap,又称分裂合并 treap,支持维护序列,可持久化等特性. FHQ-treap 有两个核心操作,分裂 与 合并.通过这两个操作,在很多情况下可以比旋转 treap ...

  5. eclipse module-info.java文件

    module 本模块的名称{ exports 对外暴露的包路径; requires 需要依赖的其他模块名称; } module-info.java不是类,不是接口,是一些模块描述信息.module也不 ...

  6. vs(visual stuiod)中vc++工程的Filter和Folder及vcxproj知识

    vs中创建Filter 在一个新项目中右键 - Add - New,默认只有一选项 New Filter. 创建出来的Filter可以理解为是VS的过滤器(虚拟目录),它不会在本地的磁盘上新建目录,而 ...

  7. 【分享】如何才能简洁高效不失优雅的爆破ZIP文件?

    0x01 前言 在CTF比赛中,压缩包密码的爆破一直是一个热门话题.在这个过程中,简洁高效的方法是至关重要的.本文将介绍一些实用的技巧和工具,帮助您高效地爆破ZIP文件密码,而不失优雅.我们将探讨一些 ...

  8. GitOps 与 DevOps:了解关键差异,为企业做出最佳选择

    在软件开发领域,GitOps 和 DevOps 是加强协作和实现软件交付流程自动化的重要技术.虽然这两种模式都旨在提高软件开发生命周期的效率,但它们的核心原则和实施方式却各不相同. 本篇文章将帮助您了 ...

  9. 详细讲解原生js拖拽

    场景描述 今天遇见一个问题,那就是产品希望在弹出来的窗口. 可以移动这个弹窗的位置 增加用户体验,我们直接使用的element-ui中的 Dialog 对话框 我们现在需要拖拽标题,移动元素位置 元素 ...

  10. Programming abstractions in C阅读笔记: p118-p122

    <Programming Abstractions In C>学习第49天,p118-p122,总结如下: 一.技术总结 1.随机数 (1)seed p119,"The init ...