高性能网关系统如何设计?

本文准备围绕七个点来讲网关,分别是网关的基本概念、网关设计思路、网关设计重点、流量网关、业务网关、常见网关对比,对基础概念熟悉的朋友可以根据目录查看自己感兴趣的部分。 什么是网关 网关,很多地方将网关比如成门, 没什么问题, 但是需要区分网关与网桥的区别, 网桥 工作在数据链路层,在不同或相同类型的LAN之间存储并转发数据帧,必要时进行链路层上的协议转换。可连接两个或多个网络,在其中传送信息包。 网关 是一个大概念,不具体特指一类产品,只要连接两个不同的网络都可以叫网关,网桥一般只转发信息,而网关可能进行包装。 网关通俗理解 根据网关的特性,举个例子: 假如你要去找集团老板(这儿只是举个例子), 大家都知道老板肯定不是谁想见就能见的, 也怕坏人嘛, 那么你去老板所在的办公楼,假如是集团总部, 大楼这个门就充当了网关的角色, 大门一般都有看门员 ,看门员会做哪些事情呢? 首先所有想见老板的人肯定都得从这个门进(统一入口 ), 这个门相当于将办公室和外界隔离了,主要为了保护里面的安全以及正常工作, 来到这个门之后, 门卫肯定会让你出示相关证件(鉴权检验 ), 意思就是判断你要见老板这个请求是否合理, 如果不合理直接就拒绝了, 让你回家等消息 , 如果鉴权之后, 发现你找老板其实只是为了和他谈谈两元店的生意, 门卫会跟你说这个用不着找老板, 你去集团投资部就行了(动态路由 , 将请求路由到不同的后端集群中), 此时会对你进行一些包装 ,例如给你出具一个访问证类似的,然后告诉你路该怎么走,等等。 你看看,网关的作用是不是就是这三个, 最终目的就是减少你与集团的耦合,具体到计算机上就是减少客户端与服务端的耦合,如果没有网关意味着所有请求都会直接调用服务器上的资源,这样耦合太强了,服务器出了问题,客户端会直接报错, 例如老板换工作的地方了,如果没有网关你直接去原来的地方找, 肯定会被告知老板不在这儿。 为什么需要网关 当使用单体应用程序架构时,客户端(Web 或移动端)通过向后端应用程序发起一次 REST 调用来获取数据。负载均衡器将请求路由给 N 个相同的应用程序实例中的一个。然后应用程序会查询各种数据库表,并将响应返回给客户端。微服务架构下,单体应用被切割成多个微服务,如果将所有的微服务直接对外暴露,势必会出现安全方面的各种问题,另外内外耦合严重。 客户端可以直接向每个微服务发送请求,其问题主要如下:

  • 客户端需求和每个微服务暴露的细粒度 API 不匹配。
  • 部分服务使用的协议不是Web友好协议。可能使用 Thrift 二进制 RPC,也可能使用 AMQP 消息传递协议。
  • 微服务难以重构。如果合并两个服务,或者将一个服务拆分成两个或更多服务,这类重构就非常困难了。

服务端的各个服务直接暴露给客户端调用势必会引起各种问题。同时,服务端的各个服务可扩展和伸缩性很差。API 网关是微服务架构中的基础组件,位于接入层之下和业务服务层之上,如前所述的这些功能适合在 API 网关实现。

网关与服务器集群

回到我们服务器上,下面图介绍了网关(Gateway)作用,可知 Gateway 方式下的架构,可以细到为每一个服务的实例配置一个自己的 Gateway,也可以粗到为一组服务配置一个,甚至可以粗到为整个架构配置一个接入的 Gateway。于是,整个系统架构的复杂度就会变得简单可控起来。

这张图展示了一个多层 Gateway 架构,其中有一个总的 Gateway 接入所有的流量(流量网关 ),并分发给不同的子系统,还有第二级 Gateway 用于做各个子系统的接入 Gateway(业务网关 )。可以看到,网关所管理的服务粒度可粗可细。通过网关,我们可以把分布式架构组织成一个星型架构,由网络对服务的请求进行路由和分发。下面来聊聊好的网关应该具备哪些功能,也就是网关设计模式。

网关设计思路

一个网关需要有以下的功能:

1. 请求路由

网关一定要有请求路由的功能。这样一来,对于调用端来说,也是一件非常方便的事情。因为调用端不需要知道自己需要用到的其它服务的地址,全部统一地交给 Gateway 来处理。

2. 服务注册

为了能够代理后面的服务,并把请求路由到正确的位置上,网关应该有服务注册功能,也就是后端的服务实例可以把其提供服务的地址注册、取消注册。一般来说,注册也就是注册一些 API 接口。比如,HTTP 的 Restful 请求,可以注册相应 API 的 URI、方法、HTTP 头。这样,Gateway 就可以根据接收到的请求中的信息来决定路由到哪一个后端的服务上。

3. 负载均衡

因为一个网关可以接收多个服务实例,所以网关还需要在各个对等的服务实例上做负载均衡策略。简单点就是直接 Round-Robin 轮询,复杂点的可以设置上权重进行分发,再复杂一点还可以做到 session 粘连。

4. 弹力设计

网关还可以把弹力设计中的那些异步、重试、幂等、流控、熔断、监视等都可以实现进去。这样,同样可以像 Service Mesh 那样,让应用服务只关心自己的业务逻辑(或是说数据面上的事)而不是控制逻辑(控制面)。

5. 安全方面

SSL 加密及证书管理、Session 验证、授权、数据校验,以及对请求源进行恶意攻击的防范。错误处理越靠前的位置就是越好,所以,网关可以做到一个全站的接入组件来对后端的服务进行保护。当然,网关还可以做更多更有趣的事情,比如:灰度发布、API聚合、API编排。

灰度发布

网关完全可以做到对相同服务不同版本的实例进行导流,还可以收集相关的数据。这样对于软件质量的提升,甚至产品试错都有非常积极的意义。

API 聚合

使用网关可以将多个单独请求聚合成一个请求。在微服务体系的架构中,因为服务变小了,所以一个明显的问题是,客户端可能需要多次请求才能得到所有的数据。这样一来,客户端与后端之间的频繁通信会对应用程序的性能和规模产生非常不利的影响。于是,我们可以让网关来帮客户端请求多个后端的服务(有些场景下完全可以并发请求),然后把后端服务的响应结果拼装起来,回传给客户端(当然,这个过程也可以做成异步的,但这需要客户端的配合)。

API 编排

同样在微服务的架构下,要走完一个完整的业务流程,我们需要调用一系列 API,就像一种工作流一样,这个事完全可以通过网页来编排这个业务流程。我们可能通过一个 DSL 来定义和编排不同的 API,也可以通过像 AWS Lambda 服务那样的方式来串联不同的 API。

网关设计重点

网关设计重点主要是三个, 高性能、高可用、高扩展:

1. 高性能

在技术设计上,网关不应该也不能成为性能的瓶颈。对于高性能,最好使用高性能的编程语言来实现,如 C、C++、Go 和 Java。网关对后端的请求,以及对前端的请求的服务一定要使用异步非阻塞的 I/O 来确保后端延迟不会导致应用程序中出现性能问题。C 和 C++ 可以参看 Linux 下的 epoll 和 Windows 的 I/O Completion Port 的异步 IO 模型,Java 下如 Netty、Spring Reactor 的 NIO 框架。

2. 高可用

因为所有的流量或调用经过网关,所以网关必须成为一个高可用的技术组件,它的稳定直接关系到了所有服务的稳定。网关如果没有设计,就会成变一个单点故障。因此,一个好的网关至少要做到以下几点。

  • 集群化 。网关要成为一个集群,其最好可以自己组成一个集群,并可以自己同步集群数据,而不需要依赖于一个第三方系统来同步数据。
  • 服务化 。网关还需要做到在不间断的情况下修改配置,一种是像 Nginx reload 配置那样,可以做到不停服务,另一种是最好做到服务化。也就是说,得要有自己的 Admin API 来在运行时修改自己的配置。
  • 持续化 。比如重启,就是像 Nginx 那样优雅地重启。有一个主管请求分发的主进程。当我们需要重启时,新的请求被分配到新的进程中,而老的进程处理完正在处理的请求后就退出。

3. 高扩展

因为网关需要承接所有的业务流量和请求,所以一定会有或多或少的业务逻辑。而我们都知道,业务逻辑是多变和不确定的。比如,需要在网关上加入一些和业务相关的东西。因此,一个好的 Gateway 还需要是可以扩展的,并能进行二次开发的。当然,像 Nginx 那样通过 Module 进行二次开发的固然可以。

另外,在运维方面 ,网关应该有以下几个设计原则。

  • 业务松耦合,协议紧耦合 。在业务设计上,网关不应与后面的服务之间形成服务耦合,也不应该有业务逻辑。网关应该是在网络应用层上的组件,不应该处理通讯协议体,只应该解析和处理通讯协议头。另外,除了服务发现外,网关不应该有第三方服务的依赖。
  • 应用监视,提供分析数据 。网关上需要考虑应用性能的监控,除了有相应后端服务的高可用的统计之外,还需要使用 Tracing ID 实施分布式链路跟踪,并统计好一定时间内每个 API 的吞吐量、响应时间和返回码,以便启动弹力设计中的相应策略。
  • 用弹力设计保护后端服务 。网关上一定要实现熔断、限流、重试和超时等弹力设计。如果一个或多个服务调用花费的时间过长,那么可接受超时并返回一部分数据,或是返回一个网关里的缓存的上一次成功请求的数据。你可以考虑一下这样的设计。
  • DevOps 。因为网关这个组件太关键了,所以需要 DevOps 这样的东西,将其发生故障的概率降到最低。这个软件需要经过精良的测试,包括功能和性能的测试,还有浸泡测试。还需要有一系列自动化运维的管控工具。

网关设计注意事项

  1. 不要在网关中的代码里内置聚合后端服务的功能,而应考虑将聚合服务放在网关核心代码之外。可以使用 Plugin 的方式,也可以放在网关后面形成一个 Serverless 服务。
  2. 网关应该靠近后端服务,并和后端服务使用同一个内网,这样可以保证网关和后端服务调用的低延迟,并可以减少很多网络上的问题。这里多说一句,网关处理的静态内容应该靠近用户(应该放到 CDN 上),而网关和此时的动态服务应该靠近后端服务。
  3. 网关也需要做容量扩展,所以需要成为一个集群来分担前端带来的流量。这一点,要么通过 DNS 轮询的方式实现,要么通过 CDN 来做流量调度,或者通过更为底层的性能更高的负载均衡设备。
  4. 对于服务发现,可以做一个时间不长的缓存,这样不需要每次请求都去查一下相关的服务所在的地方。当然,如果你的系统不复杂,可以考虑把服务发现的功能直接集成进网关中。
  5. 为网关考虑 bulkhead 设计方式。用不同的网关服务不同的后端服务,或是用不同的网关服务前端不同的客户。

另外,因为网关是为用户请求和后端服务的桥接装置,所以需要考虑一些安全方面的事宜。具体如下:

  1. 加密数据 。可以把 SSL 相关的证书放到网关上,由网关做统一的 SSL 传输管理。
  2. 校验用户的请求 。一些基本的用户验证可以放在网关上来做,比如用户是否已登录,用户请求中的 token 是否合法等。但是,我们需要权衡一下,网关是否需要校验用户的输入。因为这样一来,网关就需要从只关心协议头,到需要关心协议体。而协议体中的东西一方面不像协议头是标准的,另一方面解析协议体还要耗费大量的运行时间,从而降低网关的性能。对此,我想说的是,看具体需求,一方面如果协议体是标准的,那么可以干;另一方面,对于解析协议所带来的性能问题,需要做相应的隔离。
  3. 检测异常访问 。网关需要检测一些异常访问,比如,在一段比较短的时间内请求次数超过一定数值;还比如,同一客户端的 4xx 请求出错率太高……对于这样的一些请求访问,网关一方面要把这样的请求屏蔽掉,另一方面需要发出警告,有可能会是一些比较重大的安全问题,如被黑客攻击。

流量网关

流量网关,顾名思义就是控制流量进入集群的网关,有很多工作需要在这一步做,对于一个服务集群,势必有很多非法的请求或者无效的请求,这时候要将请求拒之门外,降低集群的流量压力。

定义全局性的、跟具体的后端业务应用和服务完全无关的策略网关就是上图所示的架构模型——流量网关。流量网关通常只专注于全局的Api管理策略,比如全局流量监控、日志记录、全局限流、黑白名单控制、接入请求到业务系统的负载均衡等,有点类似防火墙。Kong 就是典型的流量网关。

下面是kong的架构图,来自官网:https://konghq.com

这里需要补充一点的是,业务网关一般部署在流量网关之后、业务系统之前,比流量网关更靠近业务系统。通常API网指的是业务网关。有时候我们也会模糊流量网关和业务网关,让一个网关承担所有的工作,所以这两者之间并没有严格的界线。

业务网关

当一个单体应用被拆分成许许多多的微服务应用后,也带来了一些问题。一些与业务非强相关的功能,比如权限控制、日志输出、数据加密、熔断限流等,每个微服务应用都需要,因此存在着大量重复的代码实现。而且由于系统的迭代、人员的更替,各个微服务中这些功能的实现细节出现了较大的差异,导致维护成本变高。另一方面,原先单体应用下非常容易做的接口管理,在服务拆分后没有了一个集中管理的地方,无法统计已存在哪些接口、接口定义是什么、运行状态如何。

网关就是为了解决上述问题。作为微服务体系中的核心基础设施,一般需要具备接口管理、协议适配、熔断限流、安全防护等功能,各种开源的网关产品(比如 zuul)都提供了优秀高可扩展性的架构、可以很方便的实现我们需要的一些功能、比如鉴权、日志监控、熔断限流等。

与流量网关相对应的就是业务网关,业务网关更靠近我们的业务,也就是与服务器应用层打交道,那么有很多应用层需要考虑的事情就可以依托业务网关,例如在线程模型、协议适配、熔断限流,服务编排等。下面看看业务网关体系结构:

从这个途中可以看出业务网关主要职责以及所做的事情, 目前业务网关比较成熟的 API 网关框架产品有三个 分别是:Zuul1、Zuul2 和 SpringCloud Gateway, 后面再进行对比。

常见网关对比

既然对比,就先宏观上对各种网关有一个了解,后面再挑一些常用的或者说应用广泛的详细了解。

目前常见的开源网关大致上按照语言分类有如下几类:

  • Nginx+lua :OpenResty、Kong、Orange、Abtesting gateway 等
  • Java :Zuul/Zuul2、Spring Cloud Gateway、Kaazing KWG、gravitee、Dromara soul 等
  • Go :Janus、fagongzi、Grpc-gateway
  • Dotnet :Ocelot
  • NodeJS :Express Gateway、Micro Gateway

按照使用数量、成熟度等来划分,主流的有 5个:

  • OpenResty
  • Kong
  • Zuul、Zuul2
  • Spring Cloud Gateway

1. OpenResty

OpenResty是一个流量网关,根据前面对流量网关的介绍就可以知道流量网关的指责。

OpenResty基于 Nginx与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

通过揉和众多设计良好的 Nginx 模块,OpenResty 有效地把 Nginx 服务器转变为一个强大的 Web 应用服务器,基于它开发人员可以使用 Lua 编程语言对 Nginx 核心以及现有的各种 Nginx C 模块进行脚本编程,构建出可以处理一万以上并发请求的极端高性能的 Web 应用

OpenResty 最早是顺应 OpenAPI 的潮流做的,所以 Open 取自“开放”之意,而Resty便是 REST 风格的意思。虽然后来也可以基于 ngx_openresty 实现任何形式的 web service 或者传统的 web 应用。

也就是说 Nginx 不再是一个简单的静态网页服务器,也不再是一个简单的反向代理了。第二代的 openresty 致力于通过一系列 nginx 模块,把nginx扩展为全功能的 web 应用服务器。

ngx_openresty 是用户驱动的项目,后来也有不少国内用户的参与,从 openresty.org 的点击量分布上看,国内和国外的点击量基本持平。

ngx_openresty 目前有两大应用目标:

  1. 通用目的的 web 应用服务器。在这个目标下,现有的 web 应用技术都可以算是和 OpenResty 或多或少有些类似,比如 Nodejs, PHP 等等。ngx_openresty 的性能(包括内存使用和 CPU 效率)算是最大的卖点之一。
  2. Nginx 的脚本扩展编程,用于构建灵活的 Web 应用网关和 Web 应用防火墙。有些类似的是 NetScaler。其优势在于 Lua 编程带来的巨大灵活性。

2. Kong

Kong基于OpenResty开发,也是流量层网关, 是一个云原生、快速、可扩展、分布式的Api 网关。继承了OpenResty的高性能、易扩展性等特点。Kong通过简单的增加机器节点,可以很容易的水平扩展。同时功能插件化,可通过插件来扩展其能力。而且在任何基础架构上都可以运行。具有以下特性:

  • 提供了多样化的认证层来保护Api。
  • 可对出入流量进行管制。
  • 提供了可视化的流量检查、监视分析Api。
  • 能够及时的转换请求和相应。
  • 提供log解决方案
  • 可通过api调用Serverless 函数。

Kong解决了什么问题

当我们决定对应用进行微服务改造时,应用客户端如何与微服务交互的问题也随之而来,毕竟服务数量的增加会直接导致部署授权、负载均衡、通信管理、分析和改变的难度增加。

面对以上问题,API GATEWAY是一个不错的解决方案,其所提供的访问限制、安全、流量控制、分析监控、日志、请求转发、合成和协议转换功能,可以解放开发者去把精力集中在具体逻辑的代码,而不是把时间花费在考虑如何解决应用和其他微服务链接的问题上。

图片来自Kong官网:

可以看到Kong解决的问题。专注于全局的Api管理策略,全局流量监控、日志记录、全局限流、黑白名单控制、接入请求到业务系统的负载均衡等。

Kong的优点以及性能

在众多 API GATEWAY 框架中,Mashape 开源的高性能高可用API网关和API服务管理层——KONG(基于 NGINX+Lua)特点尤为突出,它可以通过插件扩展已有功能,这些插件(使用 lua 编写)在API请求响应循环的生命周期中被执行。于此同时,KONG本身提供包括 HTTP 基本认证、密钥认证、CORS、TCP、UDP、文件日志、API请求限流、请求转发及 NGINX 监控等基本功能。目前,Kong 在 Mashape 管理了超过 15,000 个 API,为 200,000 开发者提供了每月数十亿的请求支持。

Kong架构

Kong提供一些列的服务,这就不得不谈谈内部的架构:

首先最底层是基于Nginx, Nginx是高性能的基础层, 一个良好的负载均衡、反向代理器,然后在此基础上增加Lua脚本库,形成了OpenResty,拦截请求, 响应生命周期,可以通过Lua编写脚本,所以插件比较丰富。

3. Zuul1.0

Zuul是所有从设备和web站点到Netflix流媒体应用程序后端请求的前门。作为一个边缘服务应用程序,Zuul被构建来支持动态路由、监视、弹性和安全性。它还可以根据需要将请求路由到多个Amazon自动伸缩组。

Zuul使用了一系列不同类型的过滤器,使我们能够快速灵活地将功能应用到服务中。

过滤器

过滤器是Zuul的核心功能。它们负责应用程序的业务逻辑,可以执行各种任务。

  • Type :通常定义过滤器应用在哪个阶段
  • Async :定义过滤器是同步还是异步
  • Execution Order :执行顺序
  • Criteria :过滤器执行的条件
  • Action :如果条件满足,过滤器执行的动作

Zuul提供了一个动态读取、编译和运行这些过滤器的框架。过滤器之间不直接通信,而是通过每个请求特有的RequestContext共享状态。

下面是Zuul的一些过滤器:

Incoming

Incoming过滤器在请求被代理到Origin之前执行。这通常是执行大部分业务逻辑的地方。例如:认证、动态路由、速率限制、DDoS保护、指标。

Endpoint

Endpoint过滤器负责基于incoming过滤器的执行来处理请求。Zuul有一个内置的过滤器(ProxyEndpoint),用于将请求代理到后端服务器,因此这些过滤器的典型用途是用于静态端点。例如:健康检查响应,静态错误响应,404响应。

Outgoing

Outgoing过滤器在从后端接收到响应以后执行处理操作。通常情况下,它们更多地用于形成响应和添加指标,而不是用于任何繁重的工作。例如:存储统计信息、添加/剥离标准标题、向实时流发送事件、gziping响应。

过滤器类型

下面是与一个请求典型的生命周期对应的标准的过滤器类型:

  • PRE :路由到Origin之前执行
  • ROUTING :路由到Origin期间执行
  • POST :请求被路由到Origin之后执行
  • ERROR :发生错误的时候执行

这些过滤器帮助我们执行以下功能:

  • 身份验证和安全性 :识别每个资源的身份验证需求,并拒绝不满足它们的请求
  • 监控 :在边缘跟踪有意义的数据和统计数据,以便给我们一个准确的生产视图
  • 动态路由 :动态路由请求到不同的后端集群
  • 压力测试 :逐渐增加集群的流量,以评估性能
  • 限流 :为每种请求类型分配容量,并丢弃超过限制的请求
  • 静态响应处理 :直接在边缘构建一些响应,而不是将它们转发到内部集群

Zuul 1.0 请求生命周期

Netflix宣布了通用API网关Zuul的架构转型。Zuul原本采用同步阻塞架构,转型后叫作Zuul2,采用异步非阻塞架构。Zuul2和Zuul1在架构方面的主要区别在于,Zuul2运行在异步非阻塞的框架上,比如Netty。Zuul1依赖多线程来支持吞吐量的增长,而Zuul 2使用的Netty框架依赖事件循环和回调函数。

4. Zuul2.0

Zuul 2.0 架构图

上图是Zuul2的架构,和Zuul1没有本质区别,两点变化:

  1. 前端用Netty Server代替Servlet,目的是支持前端异步。后端用Netty Client代替Http Client,目的是支持后端异步。
  2. 过滤器换了一下名字,用Inbound Filters代替Pre-routing Filters,用Endpoint Filter代替Routing Filter,用Outbound Filters代替Post-routing Filters。

Inbound Filters :路由到 Origin 之前执行,可以用于身份验证、路由和装饰请求

Endpoint Filters :可用于返回静态响应,否则内置的ProxyEndpoint过滤器将请求路由到Origin

Outbound Filters :从Origin那里获取响应后执行,可以用于度量、装饰用户的响应或添加自定义header

有两种类型的过滤器:sync 和 async。因为Zuul是运行在一个事件循环之上的,因此从来不要在过滤中阻塞。如果你非要阻塞,可以在一个异步过滤器中这样做,并且在一个单独的线程池上运行,否则可以使用同步过滤器。

上文提到过Zuul2开始采用了异步模型

优势 是异步非阻塞模式启动的线程很少,基本上一个CPU core上只需启一个事件环处理线程,它使用的线程资源就很少,上下文切换(Context Switch)开销也少。非阻塞模式可以接受的连接数大大增加,可以简单理解为请求来了只需要进队列,这个队列的容量可以设得很大,只要不超时,队列中的请求都会被依次处理。

不足 ,异步模式让编程模型变得复杂。一方面Zuul2本身的代码要比Zuul1复杂很多,Zuul1的代码比较容易看懂,Zuul2的代码看起来就比较费劲。另一方面异步模型没有一个明确清晰的请求->处理->响应执行流程(call flow),它的流程是通过事件触发的,请求处理的流程随时可能被切换断开,内部实现要通过一些关联id机制才能把整个执行流再串联起来,这就给开发调试运维引入了很多复杂性,比如你在IDE里头调试异步请求流就非常困难。另外ThreadLocal机制在这种异步模式下就不能简单工作,因为只有一个事件环线程,不是每个请求一个线程,也就没有线程局部的概念,所以对于CAT这种依赖于ThreadLocal才能工作的监控工具,调用链埋点就不好搞(实际可以工作但需要进行特殊处理)。

总体上,异步非阻塞模式比较适用于IO密集型(IO bound)场景,这种场景下系统大部分时间在处理IO,CPU计算比较轻,少量事件环线程就能处理。

Zuul 与 Zuul 2 性能对比

Netflix给出了一个比较模糊的数据,大致Zuul2的性能比Zuul1好20%左右 ,这里的性能主要指每节点每秒处理的请求数。为什么说模糊呢?因为这个数据受实际测试环境,流量场景模式等众多因素影响,你很难复现这个测试数据。即便这个20%的性能提升是确实的,其实这个性能提升也并不大,和异步引入的复杂性相比,这20%的提升是否值得是个问题。Netflix本身在其博文22和ppt11中也是有点含糊其词,甚至自身都有一些疑问的。

5. Spring Cloud Gateway

SpringCloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。

SpringCloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 2.0之前的非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。

Spring Cloud Gateway 的目标,不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

Spring Cloud Gateway 底层使用了高性能的通信框架Netty

SpringCloud Gateway 特征

SpringCloud官方,对SpringCloud Gateway 特征介绍如下:

(1)基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0

(2)集成 Hystrix 断路器

(3)集成 Spring Cloud DiscoveryClient

(4)Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters

(5)具备一些网关的高级功能:动态路由、限流、路径重写

从以上的特征来说,和Zuul的特征差别不大。SpringCloud Gateway和Zuul主要的区别,还是在底层的通信框架上。

简单说明一下上文中的三个术语:

Filter (过滤器)

和Zuul的过滤器在概念上类似,可以使用它拦截和修改请求,并且对上游的响应,进行二次处理。过滤器为org.springframework.cloud.gateway.filter.GatewayFilter类的实例。

Route (路由)

网关配置的基本组成模块,和Zuul的路由配置模块类似。一个Route模块 由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。

Predicate (断言):

这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。断言的 输入类型是一个 ServerWebExchange。

几种网关的对比

本文转载于网络 如有侵权请联系删除

相关文章

  • 如何躲避针对后门攻击的检测

    介绍这次介绍的是一篇来自于EuroS&P-2020的文章,"BypassingBackdoorDetectionAlgorithmsinDeepLearning"作者中有比较著名的大佬RezaShokri。该工作主要针对Wang等人提出来的NeuralCleanse。关于后门攻击,您可以参考我这篇文章。 关于NeuralCleanse,您可以参考我这篇文章。 开门见山该工作主要是提出一种攻击,用来躲避后门检测的,针对Wang等人提出来的神经元裁剪方法,给出了一种攻击策略。在先前的工作中,人们发现在正常样本的输入以及带有后门样本的输入下,神经元的表现是有差异的。根据这些差异,提出了一些基于神经元裁剪的策略来对后门攻击进行防御。这篇工作的主要核心在于,去尽可能地使得后门样本和正常样本的差异变小。如下图所示:arch攻击者会训练一个判别器,去判别中间的特征表示是否来自于后门样本。通过这种对抗性的设置,实现后门样本和正常样本的表现趋于一致,进而躲避掉防御。误差的设置最为核心的形式为两个部分组成,可以写成:其中是输入,是标签,是网络的参数,是关于的类别预测输出,是的隐

  • Android Studio轻松构建自定义模板的步骤记录

    前言之前其实有从鸿洋的文章有了解过AS的模板开发,一直想做一些自己经常使用的模板,以减少重复代码工作,但是发现太费劲了,所以一直搁置。然后昨天无意中发现了这个插件TemplateBuilder,然后学习了一下,基本掌握了这个插件的使用,以及快速构建自己的模板。下面来分享一下。一、TempateBuilder插件安装环境:AndroidStudio3.1.1方式1:AS内安装方式2:本地安装先去GitHub开源地址上下载插件压缩包,或者到JetBrains上的插件地址(要翻墙哦)下载。安装完后记得要重启AS才能生效!!!二、建立自定义模板以创建TestActivity模板为例。1.创建模板所需文件按照平时的Activity,及Layout文件位置来创建把。(其实也可以随便找个位置创建以下的模板文件) Java模板文件:TestSampleActivity.javapackage${packageName}; importandroid.content.Context; importandroid.content.Intent; importandroid.os.Bundle; impo

  • 月薪80k阿里架构师:给迷茫的JAVA一些中肯建议(附学习路线图)

    前言:我用了七年的时间,一步一步走到了现在,中途也有了解过其他的技术,也想过要转其他的语言,但是最后还是坚持下来走Java这条路,希望我的经历可以帮助到后来的人,要是觉得对你有帮助的话,可以关注一下和转发分享,帮助让走在Java这条路上还迷茫的同行们。刚当程序员时,我是属于那种勤勤恳恳类型的员工,工作态度用认真来形容不为过,每天我几乎是团队里最早到公司,又最晚下班的一个。而组员张工一般情况下都是准时上下班的,即使项目进度比较紧急,他也很少加班,除非是有特殊情况,他才加班。要是按勤奋程度和工作时间长短来衡量,我想我比张工积极多了。按理说,我这么积极,工作量应该比张工多才对,其实不然,领导安排给我的工作任务和张工的任务相比,我比他还要少。从张工提交的代码看,他的代码不仅规范而且很优雅,大的不说,就他能对一些基本控件进行封装处理,还做了拓展,方便给其他开发人员调用,就已经很让我敬佩不已了。而我所谓的工作时间长,其实大多时间是在重复地造轮子,一个相同的功能重复做,不仅没有做封装处理,导致冗余代码还多,有时候刚好碰到需求改动,我就把自己累得够呛,现在想想,真的活该啊。类似这样的情况不知你身边有没

  • Flutter 学习笔记 11 - 动画一

    Flutter的动画基于Animation,Widget在build方法中读取Animation对象当前值,并可以监听动画的状态改变。Animation是Flutter动画库的一个核心抽象类,要使用需要import'package:flutter/animation.dart';Animation拥有其当前值和状态(完成或停止)。Animation不知道在屏幕显示的内容,本身和UI渲染没有任何关系。Animation对象是一个在一段时间内依次生成一个区间值的类,输出可以是线性函数、曲线函数、阶梯函数或者任何函数映射。根据Animation对象的控制方式,动画可以反向运行,甚至可以在中间切换方向。Animation对象有状态,通过.value获取动画的当前值。Animation可以生成Animation<double>、Animation<Color>或Animation<Size>。AnimationController继承Animation<double>,生成double值。在屏幕刷新的每一帧,AnimationC

  • 四、正则表达式re模块 常用的匹配规则:Python 的 re 模块也可以直接用re.match(),re.search(),re.findall(),re.finditer(),re.sub()

    什么是正则表达式正则表达式,又称规则表达式,通常被用来检索、替换那些符合某个模式(规则)的文本。 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。 给定一个正则表达式和另一个字符串,我们可以达到如下的目的:给定的字符串是否符合正则表达式的过滤逻辑(“匹配”); 通过正则表达式,从文本字符串中获取我们想要的特定部分(“过滤”)。 常用的匹配规则:\w匹配字母数字及下划线 \W匹配f非字母数字下划线 \s匹配任意空白字符,等价于[\t\n\r\f] \S匹配任意非空字符 \d匹配任意数字 \D匹配任意非数字 \A匹配字符串开始 \Z匹配字符串结束,如果存在换行,只匹配换行前的结束字符串 \z匹配字符串结束 \G匹配最后匹配完成的位置 \n匹配一个换行符 \t匹配一个制表符 ^匹配字符串的开头 $匹配字符串的末尾 .匹配任意字符,除了换行符,re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符 [....]用来表示一组字符,单独列出:[amk]匹配a,m或k [^

  • 左手用R右手Python系列——动态网页抓取与selenium驱动浏览器

    但是所有这些都是基于静态页面的(抓包与API访问的除外),很多动态网页不提供API访问,这样就只能寄希望于selenium这种基于浏览器驱动技术来完成。 好在R语言中已经有了selenium接口包——RSelenium包,这为我们爬取动态网页提供了可能。我在今年年初写过一个实习僧网站的爬虫,那个是使用R语言中另一个基于selenium驱动的接口包——Rwebdriver来完成的。 当时技术不太成熟,思路也比较幼稚,我使用了导航器硬生生的遍历了500页内容,虽然最后也爬完了所有数据,但是耗时较长(将近40分钟),效率比较低。(感兴趣的小伙伴儿可以参考下上面那篇,不过实习僧的官网近期有较大改版,现在爬取难度肯定要比当初难多了!那个代码可能无法使用了) 最近抽时间学习了下RSelenium包的相关内容,这里感谢陈堰平老师在R语言上海大会现场所做《用RSelenium打造灵活强大的网络爬虫》的演讲,虽然未达现场,但是有幸看完视频版,其中的几个细节解决了我近段时间的一些困惑,这里表示感谢。 陈堰平老师主讲:《用RSelenium打造灵活强大的网络爬虫》 http://www.xueqing.tv

  • js 编译原理

    引擎:从头到尾负责整个javaScript程序的编译过程和执行过程。 编译器:负责语法分析以及代码的生成。 作用域:负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。   vara=1; 复制 上面的代码首先会经过编译器编译,编译成vara;a=1;两部分。 编译器首先会遇到vara,编译器首先会在作用域中进行查找,如果找到该变量则会忽略改变量,继续往下编译,如果未找到该变量,编译器会在该作用域下声明一个新的变量,并命名为a. 编译器继续往下执行,遇到a=1,他会把这个编译成引擎能够运行的代码,这些代码来进行a=1,这个操作。 引擎找到编译后的代码,首先会在作用域中查找变量a,如果找到变量a,进行赋值操作,如果没找到,继续往上层作用域进行查找,如果顶层作用域还是没有查找到改变量,引擎会抛出异常。 编译器将a=1编译之后,引擎在作用域查找的过程我们称为(RHS),进行赋值的过程叫做(LHS)。我们来看一个例子 functiontest(item){ varb=0;  returnb; } t

  • JavaScript概述

    JavaScript是一种直译式语言。 什么是脚本语言? java源代码--》编译成.class文件---》java虚拟机中才能执行。 脚本语言:源码--》解释执行。 js有浏览器来解释执行。 HTML:决定了页面的框架。 CSS:用来美化我们的页面。 JS:提供用户的交互。 JS的组成: ECMAScript:核心部分,定义js的语法规范。 DOM:documentobjectmodel文档对象模型,主要是用来管理页面的。 BOM:browserobjectmodel浏览器对象模型,前进,后退,页面刷新,地址栏,历史记录,屏幕宽高。 JS的语法: 变量是弱类型:vari="放大镜看" 区分大小写。 语句结束之后的分号可以有也可以没有。 写在script标签。 JS的数据类型: 基本类型:stringnumberBOOleanundefinenull 引用类型:对象内置对象 类型转换: js内部自动转换 js的运算符和语句: 运算符和java一样。 js的输出: alert()直接弹框 document.write()向页面输出 console.log()  控制台输出 innerHT

  • Dubbo 源码分析 - 服务调用过程

    注:本系列文章已捐赠给Dubbo社区,你也可以在Dubbo官方文档中阅读本系列文章。 1.简介 在前面的文章中,我们分析了DubboSPI、服务导出与引入、以及集群容错方面的代码。经过前文的铺垫,本篇文章我们终于可以分析服务调用过程了。Dubbo服务调用过程比较复杂,包含众多步骤。比如发送请求、编解码、服务降级、过滤器链处理、序列化、线程派发以及响应请求等步骤。限于篇幅原因,本篇文章无法对所有的步骤一一进行分析。本篇文章将会重点分析请求的发送与接收、编解码、线程派发以及响应的发送与接收等过程,至于服务降级、过滤器链和序列化大家自行进行分析,也可以将其当成一个黑盒,暂时忽略也没关系。介绍完本篇文章要分析的内容,接下来我们进入正题吧。 2.源码分析 在进行源码分析之前,我们先来通过一张图了解Dubbo服务调用过程。 首先服务消费者通过代理对象Proxy发起远程调用,接着通过网络客户端Client将编码后的请求发送给服务提供方的网络层上,也就是Server。Server在收到请求后,首先要做的事情是对数据包进行解码。然后将解码后的请求发送至分发器Dispatcher,再由分发器将请求派发到

  • CentOS 设置 yum源

    什么是yum Yum(全称YellowDogUpdater)是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器。基于RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软件包.   常用命令行命令 安装软件(以foo-x.x.x.rpm为例): yuminstallfoo-x.x.x.rpm 复制 删除软件: yumremovefoo-x.x.x.rpm 复制 升级软件: yumupdatefoo 复制 查询信息: yuminfofoo 复制 检查可更新的包: yumcheck-update复制   常用的yum源地址 网易开源镜像站:http://mirrors.163.com/ 阿里云开源镜像站:http://mirrors.aliyun.com/ 中科大的Linux安装镜像源:http://centos.ustc.edu.cn/ 搜狐的Linux安装镜像源:http://mirrors.sohu.com/ 北京首都在线科技:http://mirrors.yu

  • Golang MongoDB Driver 更新符合条件的数组元素的字段

    在MongoDB的Shell里修改文档里某个符合条件的数组里的值的字段,可以这样: db.collection.updateMany( {<queryconditions>}, {<updateoperator>:{"<array>.$[<identifier>]":value}}, {arrayFilters:[{<identifier>:<condition>}]} ) 复制 而在GoLang中我们需要使用MongoDBDriver。 比如有一个Collection里每个文档是这样的: { "name":"..", "array":[]{ { "name":"a", "detail":"....", }, { "name":"b", "detail":"....", } } } 复制 我们要修改name为x的文档里面array里name为b的记录的detail信息为"test"。可以这样写: filter:=bson.M{"name":"x","array.name":"b"} update:=bson.M{"

  • 支付宝开源非侵入式 Android 自动化测试工具 Soloπ

    Soloπ(SoloPi)是支付宝开源的一个无线化、非侵入式的Android自动化测试工具,公测版拥有录制回放、性能测试、一机多控三项主要功能,能为测试开发人员节省宝贵时间。 本文是SoloPi团队关于项目的深度解读, 作者:乔瑞凯,蚂蚁金服高级无线开发工程师 前言 近年来,随着移动互联网的蓬勃发展,移动测试技术也取得了长足的进步,从早期基于测试脚本的单机自动化,到录制回放、图像识别、云测平台等测试技术贴合实际业务需求深度应用和创新,测试效率从而一次又一次被提升。 本文主要介绍支付宝在移动端上实现的一套无线化、非侵入、免Root的Android专项测试方案Soloπ。直接操控手机,即可实现自动化的功能、性能、兼容性、以及稳定性测试等工作。 移动测试1.0时代 移动测试1.0时代,也可以称之为探索期。由于厌倦了日复一日的手工操作,如何提升测试效率成为了移动测试领域最重要的课题,在此期间,除了Monkey、UiAutomator、Instruments等官方提供的工具,业界还涌现了一批优秀的开源自动化测试工具/框架,在自动化驱动能力的基础之上,不仅可以实现基本功能的验证,还可以结合性能

  • 重构的步骤

    1.找到壞味道:透過靜態程式碼分析等工具,找到需要重構的部份。2.確認人不是我殺的:確定現行程式碼可以正常運作,我們目標只是在重構,不是在bugfix或需求異動。3.錄影存證:針對可正常運作的網頁,建立seleniumtest,並且針對希望驗證的部分,加上Assert。4.說人話:打開程式碼,靜下心來了解這段程式碼的目的與意義,抽象地來思考每一段程式碼代表的每一件事,並進行排版、重新命名以及增加註解,提昇可讀性,讓自己下次可以快速了解這段程式碼的意義。5.垃圾分類:針對程式碼所代表的每一件事,透過重構技巧:擷取方法,依據人話來定義function名稱。讓context端僅剩下一堆會說話的function,讓程式碼抽象的意義浮現出來,而不需要看到太多細節。6.職責分離:找出誰,做什麼事。以當下物件的角度為出發點,確認哪一些職責是屬於當下物件,哪一些職責屬於其他物件。並透過分離function中的主詞與動詞,來建立對應的物件與行為。7.找出需求:把不屬於當下物件的職責都委託給其他物件,接著就是針對當下物件的需求,定義出物件應該需要提供哪些行為。當下物件定義好需求的行為後,不需了解其他物件背

  • Datatable 中的数据查询(查询不重复记录)

    http://blog.csdn.net/lovexiaoxiao/article/details/3734932 //在sql中我们使用distinct查询不重复记录   //然而我在项目中表关系的设置,就不能使用distinct,必须把数据读出来以后才能进行查询,下面写了一个方法用Dataview实现这个功能   privatevoidtest()   {       DataTabledt=newDataTable();       //定义表结构       dt.Columns.Add("id",typeof(System.Int32));       dt.Columns.Add("name",typeof(S

  • 科目二系统自动检测与评判要求

    考试车辆模型测绘图 小型汽车模型测绘点示意图见下图,图中2号点~12号点、14号点~24号点分别分布于车身左侧和右侧,应能反映车身轮廓特征和车宽尺寸。 通用要求 检测评判项 判定结果要求 检测精度要求 不按规定使用安全带 不合格 不按考试员指令驾驶 不合格 启动发动机时挡位未置于空挡(驻车挡) 不合格 发动机启动后,不及时松开启动开关 每次扣10分 时间大于2s,时间检测误差:0s~+0.5s 因操作不当造成发动机熄火一次 扣10分 注意:通用要求适用于从科目考试开始到考试结束的整个过程 倒车入库 检测评判项 判定结果要求 检测精度要求 不按规定路线、顺序行驶 不合格 车身出线 不合格 距离检测误差:边线基准线向外0mm~+50mm 倒库不入 不合格 在倒车前,未将两个前轮触地点均驶过控制线 不合格 项目完成时间超过210s 不合格 从倒车开始至回到起始点且两个前轮触地点均驶过控制线,时间检测误差:0s~+1s 中途停车时间超过2s 每次扣5分 时间检测误差:0s~+0.5s 侧方停车

  • vue基础知识2

    vue基础知识 <!DOCTYPEhtml> <htmllang="en"> <head> <metacharset="UTF-8"> <title>Title</title> </head> <body> <divid="app"> <h1v-if="type=='A'">A</h1> <h1v-else-if="type=='B'">B</h1> <h1v-else>No</h1> </div> <scriptsrc="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script> <script> varvm=newVue({ el:'#app', data:{ type:'B' } }) </script> </body> </html> 复制     

  • 字符串String反转,数组反转-20200623

    我发现现在笔试题经常出现字符串反转,下面记录一下常用的几种方案   1、使用String.cahrAt(i) publicstaticvoidmain(String[]args){ reverse("ABCDEFG"); } publicstaticvoidreverse(Stringinput){ System.out.println(input); Stringout=""; for(intx=input.length();x>0;x--){ out+=input.charAt(x-1); } System.out.println(out); } 复制    2、使用StringBuffer或者StringBuilder的reverse()方法 publicstaticvoidmain(String[]args){ reverse1("ABCDEFG"); } publicstaticvoidreverse1(Stringinput){ System.out.println(input); StringBuildersb=newStringBuilder(

  • “线程安全的” Dictionary(TKey,TValue)

    这是一篇翻译,专门介绍Dictionary线程安全问题,原文网址如下 http://www.grumpydev.com/2010/02/25/thread-safe-dictionarytkeytvalue/ 翻译的不对之处,请指正。 介绍   一个宠物项目,我目前正在研究中需要使用内部字典来存储“注册”的数据,这是一个相当普遍的要求。对于这个特殊的项目,.net3.5中,我想至少尝试使其“线程安全”的, 着眼于将其移动到ConcurrentDictionary,.NET4中保证线程不仅是安全的,而且具有更精细的锁提高多线程性能。   一个再简单不过的例子,却有许多人犯着这样的错误。   1.只是锁定写入? 很明显,我们需要围绕着写同步原语(syncronisationprimitive)类型的操作,但第一印象可能会使你认为读应该没问题-----尤其是如果我们坚持优先TryGetValue模式,而不是“如果它存在,那么得到的值”:   objectmyValue;//Thisisobviouslynotthreadsafe. //Somethi

  • springmvc静态资源的访问三种设置

    springmvc静态资源的访问我们在web.xml文件中配置的核心控制器的url-pattern是什么?/不能匹配.jsp.js.css.png.....后缀如果我们的项目中需要这些文件,是不能使用的!所以需要设置静态资源的访问!解决的方式3中1.Tomcat服务器中找到conf/web.xml中配置默认不拦截<servlet-mapping><servlet-name>default</servlet-name><url-pattern>/</url-pattern></servlet-mapping>在web.xml文件中配置<servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.css</url-pattern></servlet-mapping><servlet-mapping><servlet-name>default</s

  • Day-15 面向对象02 成员

    一、类的成员-变量   在类中变量分成两大类:     1.实例变量(字段)     2.类变量(静态变量) classPerson: #类变量,表示所有的该类的对象都共享这个变量. country="中国" def__init__(self,name,num,birthday): #实例变量(字段)表示你创建的每一个人都有这三个变量 self.name=name self.num=num self.birthday=birthday p1=Person("alex",18,"1840年06月01日") print(p1.name)#alex print(p1.country)#中国 p2=Person("wusir",28,"1894年07月25日") print(p2.name)#wusir print(p2.country)#中国复制   总结:     实例变量:给对象用的     类变量:多个对象共享的,最好是用类名来访问,这样更加规范   二、类的成员-方法   1.成员方法(实例方法) classComputer: #实例方法(成员方法) defp

  • php文件加密(screw方式)

    1、上传已经生成好的执行文件。 2、上传扩展文件到目录: /usr/lib64/php/modules复制 3、上传配置文件到目录: /etc/php.d复制 4、执行 ./screwa.php复制 生成加密后的文件 4、重启php-fpm。 systemctlrestartphp-fpm复制 相关文件下载

相关推荐

推荐阅读