导语
微服务产品团队为了宽广开发者敌人们能够更好的应用腾讯云微服务产品,将继续为大家提供微服务上云疾速入门的指引性文档,内容通俗易懂易上手,本篇为本系列的第二篇,为开发者敌人们详解高并发场景里限流的解决方案,欢送大家收看。
本篇文章将从以下四个方面为大家详解高并发场景限流解决方案:
- 秒杀场景架构概述
- 限流实现原理及计划选型
- 限流配置实际
- 云书城沙盒环境演示
秒杀场景架构概述
场景特点
在电商行业里,商家常常会做商品促销的流动,来进行品牌推广或吸引更多客户拜访,在这种大促的场景下,通常会有高并发流量进入零碎,也就是咱们俗称的秒杀场景。在这种场景下,个别会遇到四个典型的特色。
- 刹时申请量大,商品价格低廉,吸引大量用户在流动开始时进行抢购。
- 热点数据,指定局部商品参加流动,大量用户浏览量绝对集中。
- 防止超卖,因商品让利较多,商家为管制老本,所以数量无限。
- 不能影响其余业务,秒杀流动同时其余业务也须要失常进行。
在遇见以上特色带来的技术难题时,要如何保证系统失常运行呢?次要有以下几个设计要点:
- 秒杀子系统与主站资源隔离;
- 零碎须要具备限流能力,可能消化掉秒杀开始霎时的微小流量;
- 零碎须要具备疾速扩大能力;
- 削峰填谷,防止写流量压垮数据库;
- 热点商品提前缓存,通过缓存承载读流量;
- 库存增减须要保证数据一致性。
架构指标
为保障流动的顺利开展,业务零碎稳固,须要对承载高并发流量的架构进行正当革新。通常来讲,革新后的架构须要具备如下三个特点:
- 高性能:可能承载秒杀时较高的读写流量,保障响应时长在可承受的范畴内,并兼顾数据一致性。
- 高可用:保证系统不宕机,即便产生故障,过载爱护也能将故障管制在小范畴内,不会影响外围业务运行。
- 高扩大:零碎具备程度 / 垂直扩大能力,防止单个服务成为性能瓶颈。
革新示例
下图是一个常见的电商平台架构,从上到下别离是流量链路路径的客户端、接入层、应用层以及数据层。在这样一个典型的架构上咱们该如何革新,以实现高并发承载能力呢?
参考上图,首先会在③地位,接入层网关对南北流量的超额局部限流,防止后端系统过载,保障业务失常运行。
接下来,①、②、⑦这里进行读缓存的优化:①地位,客户端对局部变动不灵敏的数据进行本地缓存,缩小后端读取压力;②地位,CDN 缓存图片、CSS、JS 等动态文件,就近减速拜访,缩小后端读取压力;⑦地位,Redis 缓存热点数据,分担数据库查问压力,这三局部都是为了实现读优化的性能革新。
写数据的优化,在⑤地位,常应用 MySQL 进行读写拆散形式部署,多实例晋升读写性能。如单实例遇到性能瓶颈时,也可同时利用程度分库分表的形式晋升并发能力;⑥地位,应用音讯队列进行异步解耦,以削峰填谷的形式管制申请处理速度。
最初,在④地位,应用层服务反对纵向或横向扩大,晋升应用服务响应能力。微服务之间采纳熔断降级的策略,实现容错解决,防止群体故障。
以上各实际均有助于解决高并发问题,但在理论设计中,架构师须要依据申请 QPS 量级,采纳计划组合的模式逐步推进。具体落地进度,也要依据革新老本、资源老本、性能晋升回报率等因素进行综合评估。如下图所示。
限流实现原理及计划选型
接下来咱们会重点介绍阶段一和阶段二里的高并发限流能力。
什么是限流呢?限流是高并发零碎中,对于服务提供方的一种常见的爱护伎俩。通过管制 QPS 的形式,把后端服务无奈接受的局部流量回绝掉,只将可能稳固解决的流量放入进来,防止后端服务被刹时的流量顶峰冲垮,在南北向设置阈值,保障大后方的稳定性。
利用场景
- 商品秒杀 :爱护网站不被高并发拜访击垮;
- 防歹意申请 :避免歹意用户发送虚伪流量,影响失常业务拜访;避免注入、篡改或 DDos 攻打等;
- 反爬虫 :爱护外围数据不被获取。
超额流量解决形式
- 返回失败 :HTTP 429 Too Many Requests;
- 降级解决 :自定义动态页面返回;
- 申请排队 :阻塞申请,一段时间后再持续解决,实现限速。
限流计数器
在限流利用的开发中,有多种代码逻辑实现,咱们最常见的就是限流计数器。
固定窗口计数器(Fixed Window)
办法 :通过单位工夫设置,如秒、分钟、小时,采纳离散计数的办法,统计这个时间段里的流量值,一旦申请大于阈值可接受范畴,就会将这个申请回绝掉。这种实现计划简略,而且内存优化,申请会在本人所属工夫单位里计算,不会呈现跨时间段的“阻塞景象”。
问题 :因时间段临界点问题,导致统计后果可能有偏差。以 1s 限定 1000 申请为例,在上一个统计工夫的后 0.5s 进入了 1000 个申请,在下一个统计工夫的前 0.5s 也进入了 1000 个申请,因为工夫的连续性,理论 1s 内申请达到了 2000,那限流是不合乎预期的。
滑动窗口模式计数器(Sliding Window)
办法 :对固定窗口的一种改良,原理相似 TCP 拥塞管制。将工夫单位整合为多个区间,在区间内统计计数,统计区间逐渐进行窗口滑动,解决临界点问题。
问题 :内存占用较大,申请及工夫戳需保留。
限流计数器设计缺点
一般来讲,限流计数器实用于否决式限流,无奈进行排队式限流,对流量“整形”,实现削峰填谷无能为力。
如果须要这样的性能,应该如何改良呢? 最直观的想法,就是应用队列,将超额的流量进行暂存,提早进行解决。实现这种能力的算法模型,就是咱们相熟的漏桶算法。
漏桶算法
漏桶算法(Leaky Bucket)
如上图所示,网络流量和水流一样,一直的进入到零碎,当咱们零碎的可承载能力很小的状况下,咱们能够将超额的水在一个桶里暂存起来。当零碎解决完后面的流量当前,前面的流量就会接着进行解决,这就起到了削峰填谷、流量限速的作用。上面为示意代码。
漏桶 Golang 示意代码
requests := make(chan int, 5)
for i := 1; i <= 5; i++ {requests <- i}
close(requests)
limiter := time.Tick(200 * time.Millisecond)
for req := range requests {
<-limiter
fmt.Println("request", req, time.Now())
}
能力 :以固定的速率管制申请的访问速度;反对阻塞式限流;采纳 FIFO 队列,实现简略。
问题 :当短时间内有大量申请时,速率无奈动静调整。即便服务器负载不高,新申请也得在队列中期待一段时间能力被响应,无奈在固定工夫内承诺响应,容易呈现申请“饥饿”景象。
那这种问题又该如何解决呢?能够用到一个叫做令牌桶的算法。
令牌桶算法(Token Bucket)
令牌桶算法和漏桶算法的最大区别,在于这个桶里装的不再是申请,而是“通关”的令牌。每一个申请过去当前,都在队列里排队。当咱们的监控零碎发现大量申请到来,能够人为的减少通关令牌,疾速的消耗掉这一大波的申请,使新进来的申请不会期待太长时间,从而造成饥饿景象。能够看一下上面的示意代码:
令牌桶 Golang 示意代码
limiter := make(chan time.Time, 3)
for i := 0; i < 3; i++ {limiter <- time.Now()
}
go func() {for t := range time.Tick(200 * time.Millisecond) {limiter <- t}
}() // token depositor, dynamic rate
requests := make(chan int, 5)
for i := 1; i <= 5; i++ {requests <- i}
close(requests)
for req := range requests {
<-limiter
fmt.Println("request", req, time.Now())
}
单机限流 vs 分布式限流
单机限流
概念 :针对单个实例级别的限流,流量限额只针对以后被调实例失效。每一个实例都会有一个本人的限流值,当申请达到这个实例后,会进行计数,一旦超过当初能够承受的阈值后,就会间接拒绝请求。
问题 :当咱们做一个生产环境部署的时候,必定不会只有一个网关,可能会有五个、十个,一个集群的网关。那么这个集群外面的每一个实例,它是没有全局感知的,每个实例都只能看见本人的限流值,无奈达到共识,这种状况下很可能限流并不合乎预期。
分布式限流
概念 :针对服务下所有实例级别的限流,多个服务实例共享同一个全局流量限额。
办法 :将所有服务的统计后果,存入集中式的中间件中,罕用缓存实现如 Redis,etcd,以实现集群实例共享流量配额;通过分布式锁、信号量或原子操作等管制办法,解决多实例并发读写问题。
衍生问题 :获取配额会减少网络开销,解决能力会有所升高,如何解决?集中式限流中间件不可用时,流量如何应答?
分布式限流实现思路
咱们先来看看,实现一个简略的分布式限流,步骤会有哪些:
- 发令牌的过程,和各个限流过程,通过对立中间件(如 Redis、etcd 等)进行交互;
- 发令牌过程在中间件上设置限流过程个节点;每个节点里,按阈值(如分钟)设置该节点的“令牌”数量,如 key = /token/ 1 或者 /token/2,value = ratelimit = 10;
- 限流过程将节点 value,作为令牌应用,获取当前做原子性减一操作;
- 一旦过程以后节点 value 不够,依照环形拜访形式,应用下一个限流过程的令牌。
问题 :方才下面提到的两个衍生问题,在这样的实现下,如何解决呢?
计划 :
- 各限流器对中间件,能够进行 batch 更新,通过就义局部准确率,换取拜访压力缩小,进步流控性能。
- 当中间件不可用时,以后实例可配置回绝所有申请,或让流量失常通过。也可配置选用本地配额进行短路解决。
限流计划选型
Redis | 应用“INCR“和“EXPIRE”进行代码实现,或应用 redis-cell 模块 |
---|---|
Nginx | 官网限速模块,采纳漏桶算法实现•limit_req_module: 限度 IP 在单位工夫内的申请数•limit_conn_module: 限度同一时间连接数 |
云原生网关(如 Kong,APISIX) | 以插件的形式提供,反对多种限流计划,反对分布式限流•Kong:固定窗口,滑动窗口,令牌桶•APISIX:固定窗口,漏桶 |
服务治理核心(如北极星,Istio,Sentinel) | 同时提供限流和熔断性能,且可对服务间流量进行细粒度治理,如就近拜访等 |
限流配置实际
接下来咱们看一下,在云原生网关上如何配置限流。
演示视频:https://v.qq.com/x/page/b3364…
云原生网关限流
云原生劣势
- 缩小自建网关的运维老本;
- 升高服务器资源老本;
- 100% 兼容开源网关 Kong 的 API。
限流配置
- 反对秒、分钟、小时、天、月、年等多工夫维度独自或组合配置限流值;
- 反对匀速排队;
- 反对自定义返回的能力,设置返回状态码、返回内容和返回头;
- 反对按 consumer、credential、ip、service、header、path 等多个维度进行限流。
限流统计策略
- Local:计数值保留在 Nginx 本地内存中,性能最高,不适宜集群部署模式(单节点部署时举荐);
- Cluster:计数值保留在 Kong 的数据库 PostgreSQL 中,性能较差,不适宜高并发场景;
- Redis:计数值保留于内部 Redis,适宜集群部署场景,性能较高,须要额定的 redis 组件(集群部署时举荐)。
服务治理核心限流
对应的,在服务治理核心—北极星上配置限流。
演示视频:https://v.qq.com/x/page/t3364…
接入层服务流量治理
- 反对服务 / 接口 / 标签的限流能力;
- 反对疾速失败及匀速排队两种解决形式;
- 反对秒、分钟、小时、天等工夫微服务间的限流能力。
服务间调用流量治理
- 故障熔断,基于服务调用的失败率和谬误数等信息对故障资源进行剔除。
- 拜访限流,反对服务 / 接口 / 标签多级限流能力,提前限度超过阈值的流量。
云书城沙盒环境演示
咱们模仿了一个在线的云书城。它具备多个微服务模块,比方珍藏性能、购买性能,用户治理性能,订单查问性能等。在秒杀场景下,零碎减少了一个秒杀子系统,专门为大促流动时,商品秒杀应用,先来看下架构图。
从最北向进来的流量会首先通过云原生网关,达到商城主页。接下来流量进入业务网关层,它来做后端服务间 gRPC 的调用治理,最初是各微服务性能单元,通过业务逻辑进行宰割。
接下来通过沙盒环境,演示云书城在大促期间,如何应答高并发流量的拜访。
演示视频:https://v.qq.com/x/page/b3364…
读写优化及扩缩容计划
横向扩缩容 -TKE/EKS
演示视频中有应用到扩容的性能,这里咱们简略解说一下服务扩缩容。
相熟 K8S 的同学都晓得,采纳 Scale 命令能够将服务的正本数晋升,然而在限流的场景下,或者在大促时,这样手动操作必定不事实。一个更好的计划是采纳腾讯云上 TKE/EKS 的 HPC 性能,它具备定时扩缩容的能力,针对零碎负载评估值,提前做好筹备。配合 HPA 性能,针对 QPS 或零碎负载进行动静的调整,将服务的承载能力,维持在一个正当的水位上。HPC 与 HPA 相配合,根本能够做到流量顶峰时主动扩容,防止零碎解体;流量低谷时主动缩容,节约老本。
产品劣势
- HPC 组件的作用:定时执行 pod 扩容或缩容的动作。
- 抉择 HPC 的起因:秒杀场景具备流量霎时爆发式增长的特色,HPA 组件扩容须要 1 分钟左右,这段时间可能导致服务解体,HPC 组件能够依据秒杀开始工夫设定提前扩容。HPA 组件能够作为补充,造成双重保障。
- 节点池配置:无需当时购买节点,因资源有余而无奈调度实例时,实现主动扩缩容,节约老本。
异步解耦 -TDMQ Pulsar
在秒杀场景里,常常会对写申请和读申请进行优化。
写申请的优化,咱们个别会想到,通过异步的形式来解耦数据层的拜访,而不是间接将申请打到数据层上,因为数据层可能是零碎里最单薄的一个环节。咱们将一些订单的解决或者用户购买信息的解决,放在音讯队列里,这种设计逻辑和网关限流排队是统一的,指标都是以可控的形式,将零碎内部的申请,维持在可接受范畴内。
腾讯云有一款产品叫做 TDMQ Pulsar,它以存算拆散形式实现,对于疾速扩容更有劣势,产品保障了上方的计算层处于一个无状态的部署模式,对于本身的扩容速度是十分快的。另外 TDMQ Pulsar 数据层的设计机制,使得它和 Kafka 的最大区别,在于不限度 Topic 分区数,这样咱们能够启动更多的 Consumer 来晋升生产吞吐量。在秒杀场景中解决订单的生产,是会十分有帮忙的。
产品劣势
- 采纳 BookKeeper 协定实现数据强一致性;
- 存算拆散的架构带来灵便的横向扩大能力;
- 高性能低提早,单集群 QPS>10 万;
- 不同于 Kafka,TDMQ Pulsar 的消费者数量不受限于 Topic 的分区数,可启动更多的消费者晋升解决能力;
- 反对全局 / 部分程序音讯、定时音讯、延时音讯,满足各种业务需要。
热数据缓存 -TDSQL Redis
下面说了写申请的优化,接下来再说一下读申请的优化。
读申请后面提到,能够在客户层进行缓存,也能够在 CDN 层进行缓存,但更重要的是须要在数据库前也进行一次缓存,使得读申请不会间接达到零碎最单薄的环节——数据库,造成一个“抵触缓存带”。这里咱们常将一些热点数据放在 Redis 里来供大促期间应用。
产品劣势
- 超高性能;标准版 10 万 +QPS;集群版反对千万级 QPS。
- 主动容灾切换;双机热备架构;主机故障后,拜访秒级切换到备机,无需用户干涉。
- 在线扩容;控制台一键操作扩容;扩容过程中无需停服。
标准版架构
集群版架构
总结
除了本文提到的高并发秒杀场景外,在互联网服务的很多场景下,当零碎心愿实现高可用、高性能、高扩大的设计指标,都会应用到腾讯云云原生网关产品所提供的能力,比方灰度公布、全链路染色、多环境路由和多活容灾等架构。
云原生网关(Cloud-Native Gateway)是腾讯云基于开源网关 Kong 推出的一款高性能高可用的网关产品,100% 完满兼容开源。同时提供 TKE/EKS 集群直通,Nacos/Consul/Polaris/Eureka 注册核心对接,实例弹性扩缩容等能力,并有特色能力插件加强,显著缩小用户自建网关带来的开发及运维老本。另外,多可用区部署的模式,也保障了业务连续性,防止单可用区故障带来的服务中断。
无论在微服务架构下还是传统 Web 架构下,云原生网关都能以流量网关、平安网关和服务网关所须要的各种能力个性,为业务云上部署提供助力。