据说在弹簧云网关问世之前,由于Zuul 1.X中存在一些问题,例如阻塞性API,我们不支持WebSocket等,因此,由于Zuul 1.X中存在一些问题,因此弹簧云的微服务世界必须是Netflix Zuul。受到人们的批评,新版本的Zuul依靠Netflix。经过几票之后,Spring开源社区决定推出LaunchYour自己的网关组件取代Netflix Zuul。
从6月18日由Spring Cloud发布的Finchley版本开始,Spring Cloud Gateway逐渐出现。它基于诸如Spring 5.0,Spring Boot 2.0和Project Reactor之类的技术,它们不仅支持响应和未备份的API,还支持WebSocket,and and。2.x版本,在底部使用异步非目标API,大大提高了其性能,目前,Spring并不是要继续其计划。
根据官方网站,春季云网关的主要功能如下:
可以看出,弹簧云网关可以方便地集成在弹簧云生态系统中的其他组件(例如:断路器和服务发现),并且提供了一组简单的编写断言(谓词和某些位置,也被翻译为翻译在某些地方进入谓词单词。)过滤器(过滤器)机制可以为每种路线提出特殊要求。
最近,该项目已经使用了弹簧云网关,并在其上实现了一些高级特征,例如当前限制和标记。在使用网关期间,遇到了许多挑战。研究和总结时间。本文主要了解流量限制技术。首先,我将介绍一些当前常见的限制场景和当前算法,然后介绍一些有关当前限制的开源项目,以了解其他人如何实现流量限制。最后,我将介绍我在网关的网关中的方式。将当前限制列出,并在过程中分享一些经验和遇到的坑。
缓存,降级和当前限制称为高分和分布式系统。网关自然是必不可少的,因为整个分布式系统中的第一级别。通过当前限制,可以控制服务请求的速率,从而提高系统应对大量流量的能力,从而使系统更加灵活。申请方案,例如双重11个尖峰活动,12306票抓取等。
通过上述介绍,我们可能仍然对当前限制的概念感到更含糊。限制的限制是什么?顾名思义,当前限制是限制流量,但是这里的流量是一个一般概念。如果您考虑各种场景,当前限制非常复杂,并且与特定的业务规则密切相关。您可以考虑以下常见场景:
从上面的示例中可以看出,根据不同的请求和请求资源,可以组合不同的当前限制规则。您可以根据请求的IP限制当前的当前,或者根据相应的用户或限制当前的电流,或根据特定的请求参数限制当前的当前。当前限制的对象可以是请求的频率,传输率或并发量等。两个最常见的限制对象是请求频率和并发频率。相应的当前限制称为请求频率限制和并发请求限制。传输速率限制流在下载方案中更常用。例如,某些资源下载站将限制普通用户的下载速度。只有购买成员才能加快加速。这种当前的限制方法实际上与请求的频率相似,但是只有一个限制的请求量仅限于请求数据消息的大小。本文主要介绍请求频率和并发的频率。
在系统中设计当前的限制解决方案时,考虑到设计师的问题值得一个问题。当请求者被当前限制规则拦截时,我们应该如何返回结果。基因,我们有以下三种当前限制处理:
最简单的方法是拒绝服务,直接投掷异常并返回错误消息(例如,返回HTTP状态代码429的请求太多),或将302返回到前端到错误的页面,以提示用户的资源到稍后再尝试,或者稍后再试,因为一些更重要的接口无法直接拒绝,例如尖峰,订购和其他接口,我们既不希望用户请求太快,也不想失败。消息队列可以在峰值切割和电流。第三种处理方法是降级服务。当触发当前限制条件时,底部数据将直接返回。例如,默认情况下可以返回产品清单的接口。
对于不同的系统体系结构,需要使用不同的当前限制方案。如下图所示,服务部署方法通常可以分为单位模式和群集模式:
单个机器模式的当前限制非常简单,可以基于内存直接实现,群集模式的流量限制必须依赖于“集中”组件,例如网关或重新限制限制和中间件限制。
作为整个分布式系统的入口,网关已接受了所有用户请求,因此它最适合在网关中执行当前限制。有时,盖特威层被称为访问层限制。流量没有过多的解释。nginx在这里。有关这些说明的详细信息,您可以参考NGINX的官方文档。
当前限制体系结构的另一种类型是当前的中间部分,可以将电流的逻辑沉入服务层。但是,群集中的每个服务都必须统一地将其自己的流量信息汇总到其他服务的某个地方。一般而言,Redis还有更多。REDIS提供的过期特征和LUA脚本执行非常适合当前限制。除了Redis的中间件外,还有许多可以使用的类似的分布式缓存系统,例如Hazelcast,Apache Ignite,Infinispan等。
我们可以进一步扩展上述体系结构,并将网关更改为集群模式。尽管这仍然是市场层限制架构,因为网关成为群集模式,因此网关必须依靠中部来限制电流。上面讨论了这一点。中间部分中间没有区别。
通过上述学习,我们知道当前的限制可以分为请求频率和并发量。根据系统体系结构,可以将其分为网关层限制和分布式流量限制。在不同的应用程序方案中,我们需要采用不同的当前限制算法。本节将介绍一些主流电流限制算法。
要注意的一件事是,使用合并技术也可以达到限制的目的,例如线程池或连接池,但这不是本文的重点。
固定窗口算法是最简单的当前极限算法。它根据当前条件将请求时间映射到一个时间窗口,然后使用计数器积累访问时间的数量。例如,当前的限制条件为每分钟5次在几分钟内。在内部设置柜台。添加每个请求计数器。当此时间窗口超过5时,触发了当前限制条件。当请求时间落在下一个时间窗口中(11:01:00?11:01:59)时,上一个窗口的计数器无效,当前计数器很清楚,并且计数重新启动。
计数算法非常容易实现。在展台的场景下,您可以使用原子,长达或信号量来实现计数。在分布式方案中,您可以使用Redis的Excor和Expire和其他命令,并结合eval或Lua脚本。简单实现。该算法可以使用,无论是请求频率电流还是并发流量限制。
但是,该算法的缺陷也很明显,这是一个严重的关键问题。到时间窗口,计数器将被清除,这使得当前的极限效应不够平稳。恶意用户可以使用此功能绕过我们当前的限制规则。如下图所示,我们当前的限制条件最初是每分钟5次,但恶意用户在此时间窗口的下半部分窗口启动了5个请求。11:00:00?11:00:59,然后11:01:00?11:01:59时间窗口的上半年启动了5个请求,因此我们的系统将在1分钟内完成10个请求。
为了解决固定窗口算法的关键问题,可以将时间窗口分为较小的时间窗口,然后随着时间的推移删除相应的小窗口,而不是直接通过大窗口滑动。这是滑动窗口算法。我们为每个小时窗口设置一个计数器。大时间窗口的请求总数是每个小时窗口的计数器的总和。如下图所示,我们的时间窗口为5秒,可以将其分为几秒钟,并将其分为5个小窗口。每一秒钟,时间窗口都滑了一秒钟:
每次处理请求时,都需要计算所有小窗口的计数器总和。考虑到性能问题,分开的小时窗口不应太多。例如,当前的限制条件为每小时n,可以在几分钟内将其分为60个窗口。当前限制相反,流动限制效应的不准确越多,当前问题关键问题的可能性就越大。当粒径为1时,滑动窗口算法将退化为固定的窗口算法。因为这两种算法使用计数器,它们也称为计数器算法。
进一步思考,我们发现,如果粒度是最厚的,也就是说,只有一个时间窗口,滑动窗口算法就会退化为固定的窗口算法;那么,如果我们将粒径调整为最薄的粒径会发生什么?那么,分隔的时间窗口如何最好?当时间窗口稀薄到一定级别时,这意味着在每个时间窗口中只能容纳一个请求,这样我们就可以省略计数器,只记录每个请求的时间,然后计算一段时间内的请求数量。专用实施可以参考REDIS排序的设置技术和滑动窗口日志算法。
除了计数器算法外,另一个自然限制的想法是将所有请求缓存到队列中,然后以固定速度缓慢处理。这实际上是泄漏的水桶。泄漏算法假定请求安装在枪管中,并且枪管的容量为m。当枪管已满时,请求被丢弃。
枪管的顶部是水龙头。我们的要求从水龙头流向枪管。水速从水龙头流出的是不确定的,有时很慢,有时很慢。这种快速和缓慢的交通被称为爆发流动。如果枪管中的水已满,多余的水将溢出,这等同于被丢弃。从枪管底部泄漏的水速是固定的。可以看出,泄漏算法可以平滑请求。
泄漏算法可以通过队列实现,如下图所示:
当请求到达时,请勿直接处理请求,而是将其放入队列中,然后另一个线程以固定速率从队列中读取请求并从队列中处理,以实现当前的目的。具有不同的实施方法,例如设置请求的生存时间,或将队列转换为优先级,根据请求而不是先进的优先级对优先级进行排序。当然,当队列已满时,如果队列已满,请求只能被丢弃。泄漏算法具有缺陷,在处理紧急情况时非常有效,因此人们提出以下令牌讨价还价算法。
令牌桶是最广泛使用的限制算法。它的基本想法由两个部分组成:产生令牌和消费者令牌。
令牌桶算法的图标如下:
在上图中,我们将请求放入缓冲队列中。可以看出,该部分的逻辑和泄漏算法几乎相同,但是在处理请求中,一个以固定速率处理,另一个是从枪管中获得的。
如果您仔细考虑一下,您会发现令牌桶算法有一个非常关键的问题,即枪管大小的大小。正是此参数允许Tokend Barrel算法具有处理紧急情况的能力。例如,将枪管的大小设置为100,并将令牌的速度设置为每秒10秒,因此在系统免费之后一段时间(枪管中的令牌尚未消耗,它将慢慢填充),突然50次到50次。这次,该系统可以以每秒50秒的速度直接处理。随着枪管中的令牌很快使用,处理速度将慢慢下降,这将与产生令牌的速度一致。这是令牌枪管算法和泄漏算法之间的最大差异。无论泄漏算法有多少要求,它将仅以每秒10的速度处理。如果枪管的尺寸不合理,则大型交通可能会直接压碎系统。
通过分析上述令牌的原理,通常有两种不同的实施方式。第一种方法是启动内部线程,在处理请求时从枪管中添加令牌,从桶中获取令牌,这与上图中的处理逻辑。第二种方法不取决于内部线程,而是计算到每个处理请求之前实时填充的令牌的数量,然后从桶中获取令牌。是第二种方法的经典实现。容量代表令牌枪管的大小。Refilltokensperonemillis表示填充速度,每毫秒填充多少。AbleableTokens表示象征性的枪管中剩下多少个令牌。
您可以像以下内容一样创建一个令牌枪管(枪管的大小为100,每秒产生100个令牌):
从上面的代码片段中可以看出,令牌桶算法的实现非常简单有效。只有几个变量的操作实现了完整的电流极限函数。核心逻辑在于Refill()方法。在每个消费令牌上,计算当前时间和上一个填充之间的时差,以及应根据填充速度填充多少令牌。填充令牌后,然后确定令牌请求的数量是否足够。如果还不够,请返回false,如果足够的话减去令牌编号并返回true。
在实际应用中,这种原始令牌算法通常不直接使用,这通常改进了它的一些改进,例如填充率支持动态调整,基于REDIS支持分布式分布式公式,但总体上,令牌的总数支持透支的总数,它仍然符合令牌枪管算法的整体框架。稍后我们学习一些开源项目时,我们将获得更深入的体验。
许多开源项目已经实现。本节通过学习一些开源项目来学习如何实现当前限制。
Google Guava是一个功能强大的核心库,包含许多有用的工具,例如收集,缓存,并发库,字符串处理,I/O等。在它们之类基于令牌讨价还价算法,但根据传统的代币讨价还价算法进行了改进,支持了两种不同的当前限制方法:Smooth -Warmingup(Smooth Bursty)和Smooth Warmingupto。
以下方法会产生平滑的爆发(平滑爆炸):
ratelimiter。创建(5)表示该流的能力为5,每秒生成5个令牌,每秒生成每200毫秒。我们可以使用limiter.acquire.acquire()消耗令牌。如果枪管中的令牌足够,则返回0,如果令牌不足,等待时间被阻塞并返回等待时间。我们连续询问几次:
输出结果如下:
可以看出,在创建餐厅后,最初将有一个令牌,然后每200毫秒生成一个令牌,因此第一个请求将直接返回到0,随后的请求将阻止约200毫秒。此外,SmoothBursty还具有应对紧急情况的能力,并且还允许消费使未来的令牌(例如以下示例:
将获得类似的输出:
创建电流后,只有一个初始令牌,但是我们要求10张令牌卡,但是我们看到随后的请求发现,第二个请求大约需要2秒钟才能在所采用的令牌前面透支令牌。大约2秒钟。
Guava的另一种电流支持的方式是平滑的预热流(Smooth -Smooth -WarmingUp)。您可以通过以下方法创建它:
第一个参数是每秒创建的令牌数。这是每秒2个,每500毫秒生成一个。
第一个请求是立即获取令牌,但是后续请求与流平滑的流限制完全不同。在500毫秒内生成一个令牌是合理的,但是我们发现第二个请求等待1.3,但我们等待了1.3.s,而不是0.5s,第三和第四请求已经等待了一段时间。等待时间慢慢接近0.5s,直到第五请求等待时间才变为正常。温暖的时间是我们设置的3秒。
Bucket4J是基于令牌枪管算法的强大电流限制库。它不仅支持支架 - 单位流量限制,而且还通过分布式缓存来支持分布式分布,该缓存通过JCACHE API(JSR 107)规格(例如Hazelcast,Ignite,Ignite,cooherence,cooherence,infinispan或其他相容性)来调节分布式缓存调节分布。
在使用Bucket4J之前,我们需要了解Bucket4J中的几个核心概念:
铲斗接口表示令牌枪管的特定实现,这也是我们操作的入口。它提供了诸如TryConsume和TryConsumeAndreternRemainings之类的方法,以供我们的消费token.you您可以通过以下构造函数创建桶:
带宽意味着带宽,可以理解为电流的规则。Bucket4j提供了两种创建带宽的方法:简单和经典。以下是由简单方法创建的带宽,表明枪管的大小为10,并且填充速度是是每分钟10个令牌:
简单方法的大小与填充速度相同。经典方法更灵活。它可以自定义填充速度。以下示例表明枪管的大小为10,填充速度为每分钟5个令牌:
其中,补充用于填充令牌枪管,这可以通过它定义填充速度。Bucket4j有两种类型的填充令牌:间隔策略和贪婪策略。在上面的示例中,我们使用贪婪的策略。如果间隔策略可用于创建以下内容:
SO称为的间隔策略是指每次填充所有令牌的时间,例如上述示例,每分钟填充5个令牌,如下所示:
贪婪的策略将尽可能贪婪地填补令牌。这也是上述示例。它将将一分钟分为5个较小的时间单元。
在了解Bucket4J中的几个核心概念之后,让我们看一下官方网站上介绍的一些功能:
Bucket4j提供了大量文件。建议在使用bucket4j.in添加之前,在使用bucket4j之前阅读官方文档中的基本用法和高级功能,我建议使用bucket4j限制本文的速率限制Spring MVC端点。本文详细说明了如何使用spring MVC中的拦截器和桶4J在不入侵的情况下建立业务。使用Hazelcast实施分布式当前限制;此外,使用Bucket4J限制弹簧API的费率也是一个很好的入门教程。它引入了Bucket4J的基本知识。在文章的末尾,Spring Boot启动器已集成了与Spring Boot执行器进行了集成,很容易将当前限制索引集成到监视系统中。
与Guava电流相比,Bucket4J的功能显然更好。毕竟,Guava的目的仅被用作通用工具类,而不是当前限制。使用Bucket4J基本上可以满足我们的大多数要求。它不仅支持站立电流和分布式当前限制,而且可以很好地集成监视。Prometheus和Grafana非常完美。值得一提的是,有许多开源项目,例如JHIPSER API Gateway来使用Bucket4J实现当前限制。
Bucket4J中唯一的事情是它仅支持请求频率限制,不支持当前卷限制,并且有一个点。尽管Bucket4J支持分布式流量限制,但它是基于分布式缓存系统(例如Hazelcast)实现的。来源世界。
resilience4j是一种轻巧,容易 - 使用高 - 可用的框架。使用了早期版本的春季云的学生必须听到Netflix Hystrix的声音,而Resilience4J的设计灵感来自于此。还建议每个人都使用resilience4j而不是hystrix。
弹性4J的底层使用VAVR,它是一个非常轻巧的Java函数库,使弹性4J非常适合功能编程。Resilience4J提供了功能接口或装饰器模式下的lambda表达式的封装,提供了高可用性机制的浪潮:RECARCUIT BREACERINICS:RECARACUIT BURACER BREAKER,BREACER,BREACER,,,BREDARCOINICERINCY,,BREACARCUIT BREACER,BREACER,,BREDARCUIT BREACER,,BREDARCOINICERINGY,,BREDAREBIONING,,BREDARCOINAL,OFFINGIAL,,破坏性机制速率限制器,计时器限制器和隔离。(舱壁),缓存和降级。我们在这里关注这两个函数:速率限制器和舱壁,速率限制器是请求频率限制,而Bulkhead是同时发生的。
resiliation4J提供了两种类型的电流:信号量基二重体和iTomicrateLimiter。默认地位的拉塔特酰胺仪基于信号数量。用户的每个请求将申请信号量并记录应用程序时间。允许申请允许请求。应用程序故障将限制电流。此外,这是令牌桶的算法。AtomicrateLimiter与上面的经典相似,不需要其他线程。处理每个请求时,会自动填写最后一个请求的时间和生成代币的速度。对于两者之间的差异,请参阅弹性4J中的文章速率限制内置。
resilience4j还提供了两个隔离实现:信号量布鲁克黑德和ThreadPoolBulkhead。通过信号量或线程池控制请求的并发请求的数量被转介给正式文档,此处不会在此处重复。
以下是同时使用当前限制和隔离的示例:
在功能特性方面,弹性4J比Bucket4J强得多,并且还支持并发量,但最大的遗憾是Resilience4J不支持分布式流量限制。
还有许多与Internet当前限制有关的开源项目。不可能一个人介绍他们。这里只列出了冰山的一个角落:
可以看出,当前的限制技术被广泛用于实际项目中。每个人都不厌倦实现自己当前的限制算法,新算法和新实施是无穷无尽的。
我的需求实际上非常简单,我需要同时满足两个不同的当前场景:目前限制了请求频率,并并发数量受到限制,并且可以同时满足两个不同的电流限制体系结构:站立- 单位流量限制和分布式流量限制。我们开始在弹簧云网关中实现这些类型的当前限制。通过前面引入的那些项目,我们可以利用长期补充并基本上使用更多成熟的技术。播放,在互联网上找不到现成的解决方案。在与同事讨论了几个晚上之后,我想到了一种新型的双窗户流动算法。
当文章在开始时介绍了弹簧云网关的功能时,我们注意到限制了请求率的一个表明该网关具有当前功能,但是在Spring Cloud Gateway的自己的自己的链接上有许多限制。流程,不支持并发流,它的请求频率限制也不令人满意。这些要求我们自己解决。
Spring Cloud Gateway定义了有关电流的接口比率,如下所示:
该接口是一种方法。第一个参数路由ID表示路由的ID。根据RouteID的说法,您可以获得当前相关的配置。它是IP或可以从ServerWebexChange获得的其他信息。LET在requestRateLimiterGateWayFilterFactory中遵守ISADED的呼叫逻辑:
从上面的逻辑可以看出,钥匙素接口的分辨率方法可以自定义受限制的对象。
例如,下面的hostaddrkeyresolver可以根据IP限制电流:
我们继续查看Spring Cloud Gateway的代码,发现ReTisrateLimiter仅提供一个比率的接口:
显然,它基于基于Redis的。尽管它也可以通过Redis实现立场,但总是感觉到有一些大型材料和小型材料,并且它们在没有Redis的情况下对那些环境不友好。因此,我们必须实现真正的局部当前限制。
我们从Spring Cloud Gateway的拉力请求中找到了一个新功能/本地rate -climiter,并查看可能合并到版本3.0.0.LET的新功能。LET查看此本地-Rate的实现- 候选者:localratelimit.java。可以看出,根据重新销售很有趣
有趣的是,该类有一个早期版本,该版本是基于bucket4j的:
实现方法相似。在上面,Bucket4J和弹性4J已详细介绍。我不会在这里详细介绍。但是,从这里可以看出,春季生态系统对弹性4J更加乐观,我们也可以将其介绍给我们的项目。
以上介绍了如何实现单个机器请求频率限制,然后查看分布式请求频率限制。这是相对简单的,因为弹簧云网关带有当前的限制实现,这是可以使用的Redisteelimiter对于分布式电流。其实施原理仍基于令牌枪管算法,但实现逻辑放置在LUA脚本中。我们可以在src/main/resources/meta -inf/脚本目录中找到脚本文件request_raate_limiter.lua。
Java引入令牌枪管算法时实现的经典代码几乎是相同的。在并发访问期间的限制。想象一下令牌枪管中仍有一个令牌。目前,两个请求同时提出。足以确定令牌是否足够。他们俩都认为剩下一个令牌,因此允许允许两个请求允许。
配置Spring Cloud Gateway自己的电流有两种方法。第一种方法是配置文件,例如下面所示的代码,可以限制某个路由:
其中,钥匙量分辨率使用Spel表达式#{@beanname}从弹簧容器中获取hostaddrkeyresolver对象。爆发能力代表令牌枪管的大小,补充物表示每秒填充多少令牌,这是填充速度。
第二种方法是通过以下代码进行配置:
这可能会限制一定的路线。我当前的限制规则是每分钟10个请求(理论上,应每6秒或每秒填充1/6代币),这种情况无法正确限制Spring Cloud Gateway。互联网上的问题。支持利率限制器的支持大于第二分辨率,但尚未解决。
当我们了解上面的弹性4J时,我们提到了弹性4J的功能特征。这样,如果机舱损坏并损坏了水,那么只能丢失该机舱,并且可以影响其他小屋。从造船行业的经验中学习,该模型也已引入软件行业。我们称其为舱壁模式。射岩模式通常用于隔离服务。对于某些重要的系统资源,例如CPU,内存,连接数等,您可以为每种服务设置自己的资源,以防止从消耗系统资源的所有资源中的某些异常服务。也可以用于并发流。
如前所述,Resilie4J提供了两种类型的舱壁:SemaphoreBulkhead和ThreadPoolBulkhead,这也是机舱顶部常用的两个常见实现方案:一个是具有计数的信号量,另一个是固定尺寸的线程池。考虑到多线程方案中线程切换的成本,默认情况下建议使用信号量。
在操作系统的基本过程中,我们学到了两个名词:互斥词和信号量。相互数量用于螺纹的相互排除。它与关键区域有点相似。仅具有相互排斥对象的线程具有访问资源的权限。由于只有一个相互排斥的对象,因此在任何情况下都只会访问一个线程。分享资源,从而确保可以安全地访问多线程访问和操作共享资源。信号量用于线程同步。这是荷兰科学家E.W. Dijkstra提出的概念。它不同于相互排斥。该信号允许多个线程同时使用共享资源,但它还设置了同时访问共享资源的最大线程。
以下是使用信号量限制和访问访问的一个简单示例:
在这里,我们创建了100个线程以同时执行,但是由于信号计数为10个,只有10个线程在同一时间处理请求。实际上,当计数外,除了信号量之外,在Java中,有许多类别,也有许多类别可以用作计数,例如rotodiclong或longadder,在并发电流中非常普遍,但不能提供阻塞作为信号数量:
4.4实施分布式并发限制流量限制
通过在单个计算机中实现并发限制,我们已经掌握了几种常用的方法:信号量,线程池,计数器,这些是支架上的概念-alone -Olone -Olone -Olone.So,如果有一点扩展,如果分布式信号量,分布式线程池,可以实现分布式计数器,分布式并发限制限制吗?
关于分布式线程池,这是我自己的话。互联网上没有类似的概念。更接近的概念是资源调度和分发,但感觉不像。我只是在这里忽略它。
关于分布式信号量,确实有这样的事情。例如,Apache Ignite提供了用于创建分布式信号量的Ignitesemaphore。它的使用方法与信号量非常相似。Redis的Zset的使用也可以实现分布式信号量。例如,此博客介绍的方法以及电子书“ Redis In Action”也提到了这样一个示例,可以教您如何实现计数信号量。此外,Redisson还根据基于分布式的信号量Redis,类似于信号量。分布式信号量可以轻松达到分布式并发限制,并且实现方法几乎与上面单个机器的单个机器并发相同。
最后,实施方案在分布式计数器方面也有多种多样。手术。每个请求必须由这三个步骤操作:计数器的当前值和确定超过阈值的判断将被拒绝,并且计数器的价值增加。这实际上与信号量的P操作相同,该版本对应于V操作。
因此,可以使用分布信号量和计数器来达到当前限制吗?问题当然不是那么简单。
想象一下,如果处理请求时发生异常情况,发生了什么事?显然,信号量是由线程获得的,但永远不会释放。如果请求是异常的,这将导致信号填充,最后请求将无法可用。在站点 - 单位场景中,可以轻松解决此问题,只需最终添加一个:
无论出现什么样的异常,最终将执行代码,这确保信号量将被释放。但是在分布式系统中,它并不像最终添加一个简单。这是因为这是因为可能存在的异常在分布式系统中,不一定是可以捕获的异常代码,它可能是可以折叠或不可预测的系统。
关于这个问题,我和几个同事连续讨论了几个晚上,并提出了两个解决方案:第一种方法是与TTL一起使用计数器,第二种方法是基于双窗口sliding.algorithm。
第一种方法更容易理解。我们为每个请求提供一个唯一的ID,并在Redis中编写一个钥匙值对。键是requests_xxx(xxx是请求ID),值为1,并为此键设置此键的ttl(如果您的应用程序中有很长的时间请求,例如,对于某些WebSockket请求,可能会持续几个小时,一个线程需要定期打开此键的ttl)。然后在判断并发量时,使用键命令查询以请求_*开始的键数,您可以知道当前需要多少请求。此方法可以处理服务的崩溃或重新启动的问题。由于设置了每个键,一段时间后,这些键将自动消失,并且不会有完整的显着信号。当请求量较大时,网关的性能将受到严重影响。我们可以用扫描替换键命令,并且性能会得到一些改进,但是通常,效果仍然非常令人满意。
为了响应第一种方法,我们可以进一步优化它,而无需为每个请求编写关键值,而是为每个分布式系统中的每个实例提供一个唯一的ID,并在Redis中编写一个键值对,然后在REDIS中编写一个密钥对redis.key是Instances_xxx(xxx作为实例ID),值是此实例的当前并发。类似地,我们为此键设置了一个ttl,并打开线程以定期刷新此ttl。在接收请求后,添加计数器,请求结束,并且计数器减少。这与单个机场中的处理方法相同。然而,数量的芒质。但是,由于示例的数量受到限制,因此性能得到了明显提高。
我调用双窗口滑动算法的第二种方法,与TTL计数器和滑动窗口algorithm.let结合使用,根据分钟,将时间窗口设置为时间窗口,对应于Redis中的202009051130的键,值是指示请求数量的计数器。收到请求后,将一个添加到当前时间窗口中。当请求结束后,将在当前时间窗口中减少一个。请注意,请求和请求结束的时间窗口可能不相同。此外,我们还需要一个本地列表来记录与当前实例处理的所有请求和请求相对应的时间窗口,以及将过期的请求通过计时线程小于时间窗口(例如30秒)。请求的时间窗口与当前时间窗口不一致。已过期,然后将列表中的到期请求时间更新到当前时间窗口,然后将相应的金额从上一个时间窗口移动到当前时间窗口,即,以前的时间窗口减少了x,当前的时间窗口加上X.B.由于迁移线程是定期执行的,因此过期的请求将始终移至当前窗口。最后,在当前时间窗口和最后一个时间窗口的两个时间窗口中只有数据。向后移动,因此您可以为此键设置3分钟或5分钟的ttl。并发性,因为只有两个键,您只需要使用MTET即可获得两个值。以下流程图详细描述了算法的操作过程:
有几个需要注意的细节:
作为微服务体系结构的重要组成部分,网关是男人成为男人的角色,因此在网关服务的稳定性要求和性能要求中非常高。为了确保网关服务的稳定性,几代程序员已经挺身而出,并提出了18种武术:当前,融合,隔离,缓存,降级等等。本文介绍了限制的流程限制场景和算法,以及实施源代码和可能踩踏的凹坑。尽管当前限制只是网关的一个很小的功能,但它会影响网关的所有方面,并且在系统结构的设计中非常重要。
尽管我试图从不同的角度引入流量限制,但毕竟是管道偷窥豹。我看到有很多尚未引入的内容。有限的失败。此外,前面提到的Netflix不再维护Hystrix项目,因为它们将精力投入了另一个当前限制项目并发限制。它借用了TCP充血控制算法(TCP Contenestion Control算法)以实现系统的自动电流限制。有兴趣的学生可以进入其项目主页以了解更多信息。
本文的长度很长,不可避免的是,如果有问题,我仍然希望能启发我。
资料来源:www.aneasystone.com/archives/2020/08/spring-cloud-gateway-current-limiting.html