AI 年度盘点与2025发展趋势展望,50+案例解析亮相AICon 了解详情
写点什么

Redis 集群分布式锁与 API 网关分布式限流

  • 2018-12-27
  • 本文字数:3570 字

    阅读完需:约 12 分钟

Redis集群分布式锁与API网关分布式限流

Redis 集群的历史

Redis 在 3.0 前一般有两种集群方案,一是 proxy(Twemproxy、Codis),二是使用 Sentinel(哨兵)。 通过 Sentinel 是一种使用哨兵来达到高可用的方案,而 proxy 是用于在前置上进行 sharding 用代理给后端的 redis 集群的方案,达到负载均衡的方案,在单个分片的 redis 中作主从。 因为本文要重点讲解的不是 3.0 前的方案,因此说的比较粗略。


Redis3.0 提供了官方的 Redis cluster 机制支持。主要通过内部无中心的多个节点来达到集群、高可用的作用。下面是 Redis Cluster 的架构图:


Redis Cluster 的重要机制

  1. sharding 由 Redis cluster 根据 client 调用 redis 的 key 进行 hash 取模得到一个 code,根据这个 code 放到 16384 个 slot 中。在以上的架构图中 slot1 组、slot2 组、slot3 组服务器中分别是对应的差不多 1/3 的 slot。这样就得到了根据 key 的 shading。

  2. 说到 sharding 就会说到怎么进行扩容和缩容,redis cluster 也提供了工具进行迁移。 不过由于不是一致性 hash,所以涉及到迁移数据的节点数会多于一致性 hash,但是迁移的量还是可控的,只会迁移部分以达到平均的效果。 (redis cluster 迁移详细机制需要另外详细研究)

  3. 在 redis cluster 的一个 slot 组中,采用的是主备的高可用模式,只有 master 对外提供服务,如果 master 挂掉,则 slave 会成为新的 master,由新的 master 提供服务,在切换的过程中会在短时间的 redis 服务不可用。

  4. redis cluster 进入 fail 状态的条件:

  5. 如果同时有大于 N(N 为总 slot 组数)*1/2 的 master 节点数同时挂掉,则 cluster 进入 fail 状态,因为剩下的 master 已经无法选出新的 master 节点了。 分布式系统中必须要有大于 N(N 为总 slot 组数)*1/2 的节点存在才能判断有效。 节如果有是 3 主 3 备的集群,如果同时有 2 个 master 节点挂掉了,那么集群就挂掉了。

  6. 如果一个 slot 组中的服务器已经没有备用服务器了,这时 master 挂掉后,redis cluster 也会进入 fail 状态。 因为部分 slot 的数据已经查询不到了。

  7. redis cluster 具备高可用、sharding、负载均衡的功能。

  8. 如果多个 key 想要人为控制落到一个 slot 组上,可以通过对 key 进行改造实施。即如果 key 都以{key_pre}idxxxx 这样,那么所有的 key 将是以 key_pre 去确定 slot 组,这样就达到了以{key_pre}开头的 key 都会是在一个 slot 组。

  9. 当向一个 master 中写入数据时,数据是进行迅速返回的,返回后再进行主从同步的方式向 slave 进行同步,因此这里是损失了 CAP 中的一致性的。 在将来的 redis 版本中可能会开放同步写的方式写入 slave,以维护一致性,当然这样会损失一定的写入速度。

  10. 在上文中提到的在做主从切换的时候,会有短时的不可用状态,因此会操作分布式理论 CAP 中可用性。

  11. 单个 redis 服务器上的请求是顺序执行的,因为 redis 服务器是单进程、单线程的。

分布式锁

分布式锁有很多的实现方案,通常有数据库、文件系统、zookeeper、redis。下面讲述基于 redis cluster 的分布式锁方案。

基于 redis 事务实现

严格来说这并不是分布式锁,只是通过改造可以实现锁的效果。这里并不是实现锁定其他的线程被阻塞的效果,而是如果数据被其他客户端修改了就返回失败。原理是基于 reids 的 multi 和 watch 命令。 在事务开始前对要锁到的数据进行 watch,进行业务操作后,如果发现锁定的数据已经变了,就提交失败,重新进行业务操作。 在这个方案中如果执行失败就一直反复执行直到成功,也是实现了多个 redis 客户同时修改一个数据时的协调的锁的功能。


伪代码如下:


jedis.set("balance", String.valueOf(balance));
jedis.watch("balance");
Transaction transaction = jedis.multi();
transaction.decrBy("balance",amtToSubtract);/
transaction.incrBy("debt", amtToSubtract);
List result = transaction.exec();//执行事务
if(result == null){
//重新执行事务或者其他。
}else{
//事务执行成功。
}
复制代码

基于 setNX EX 实现

Redis set key 时的一个 NX 参数可以保证在这个 key 不存在的情况下写入成功。并且再加上 EX 参数可以让该 key 在超时之后自动删除。


jedis 伪代码:


String set(String key, String value, String nxxx, String expx,longtime);
复制代码


为什么要以一代代码一个命令去实现呢,是因为要防止 NX 后突然就宕机了会产生死锁。


过期的时间的设置上要考虑几个问题:


  1. 如果时间设置比较长,如果锁定发起 server 发生宕机,那么很久都解锁不了。

  2. 如果时间设置比较短,可能会发生业务还没有做完,发生了解锁,没有起到锁定的作用。


关于解锁:在解锁时需要判断当前的锁是不是自己所锁定的,因此需要在加锁时将 key 的 value 设置为一个随机数,在解锁时进行判断。 因为在解锁时有 get 数据再判断的多个操作,因此这里也需要防止并发问题,因此使用 lua 脚本写这个操作。如以下伪代码:


 $script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
$result = $this->redis->eval(script,array($key,$val),1);
复制代码


注意一个可能发生的问题:


redis 的主从异步复制机制可能丢失数据,会出现如下场景:A 线程获得了锁,但锁数据还未同步到 slave 上,master 挂了,slave 顶成主,线程 B 尝试加锁,仍然能够成功,造成 A、B 两个线程并发访问同一个资源。

基于 lua 脚本实现

由于 redis 服务器的单进程单线程模型,因此产生这种被大量使用的分布式锁的方案。


lua 脚本通过 eval 或者 evalsha 方法进行执行,同时将脚本涉及到的 key 和参数传递给 redis 服务器执行。客户端可以通过 jedis 进行调用。 evalsha 是对脚本在 redis 服务器进行预编译,这样可以减少网络交互量,加速执行时的速度。


注意以下:


  1. 由于是分布式集群环境,如果传递了多个 key,而 key 处于不同的 slot 组服务器,那么执行将会报错。

  2. lua 脚本中由于是单进程单线程执行,因此不要做消耗时间的操作。 在简单操作的情况下,在 CPU 6 核 Intel® Core™ i7-2720QM CPU @ 2.20GHz 内存 16GB 的情况下能跑出 5 万 TPS。

  3. 如果要进行 TPS 扩容,则需要通过 key 对应的 slot 组不同,将 lua 分发到不同的 slot 组中的 redis master 服务器去执行。

  4. 程序都建议使用 evalsha 的方法去执行,这样可以提高 TPS。

API 网关分布式限流

API 网关中针对一个 API、API 分组、接入应用 APP ID,IP 等进行限流。这些限流条件都将会产生一个限流使用的 key,在后续的限流中都是对这个 key 进行限流。


限流算法通常在 API 网关中可以采用令牌桶算法实现。


必须说明一点的是分布式限流由于有网络的开销,TPS 的支持隔本地限流是有差距的,因此在对于 TPS 要求很高的场景,建议采用本地限流进行处理。


下面讨论我们应该采用 redis 的哪一种分布式锁的方案:


由于 redis 事务要得到锁的效果需要在高 TPS 时会产生大量的无效的访问请求,所以不建议在这种场景下使用。


SET NX/EX 的锁方案会产生在过期时间的问题,同时也有异步复制 master 数据到 slave 的问题。相比 lua 方案会产生更多的不稳定性。


我建议采用 lua 的方案来实施分布式锁,因为都是单进程单线程的执行,因此在 TPS 上和第二种方案没有大的区别,而且由于只是一个 lua 脚本在执行,甚至是可能纯 lua 执行可能会有更高的 TPS。 当然是 lua 脚本中可能还是会去设置过期时间,但是应用 server 宕机并不会影响到 redis 中的锁。 当然 master 异步复制的问题还是有, 但是并不会造成问题,因为数据只会有 1 个 lua 脚本执行问题,下一个执行就正常了。


在实现方案的时候使用了 Jedis 库,有一些问题在方案的实现层面我已经去做过验证了,可能也会是读者的疑问。


  1. 理论上 jedis 都应该是连接 redis 集群中的 master 节点,因为 redis 主备是采用的主备(Active-Standby 方式)的方式。 需要确定在这种情况下应该是配置所有的 redis 节点还是只配置 master。 理论上是需要配置所有的节点。


答:配置所有节点,做自动转发。


  1. 需要测试在其中一个 slot 组的 master 挂掉后,slave 成为 master 后,jedis cluster 的方式还能正常的访问对应的 key。从资料上来看是能自动处理的。


答:能自动处理。


  1. 因为是采用 redis cluster,所以需要测试 key 存储在 slot 1 组上时,jedis cluster 连接的是 slot 2 组上的 redis 服务器,在这种情况下要能正常的调用 lua。 如果不能要看怎么样调用,因为 redis 可能会返回"MOVED"指令,这个要看 jedis 组件有没有实现自动的迁移。


答:能自动处理。


  1. 需要测试连接的是 salve 节点,但是读请求是转给了 master 节点,salve 节点不处理请求。因为是主备(Active-Standby 方式)状态。


答:能自动处理,由 Jedis jar 进行维护节点的状态,查询最新的 master 节点的信息。


有兴趣的朋友可以加入 QQ 交流群:244054462,备注:API 网关架构设计交流。




作者简介


颜诗超,成都小豹科技的“小豹 API 网关”产品负责人。


2018-12-27 16:054412

评论 2 条评论

发布
暂无评论
发现更多内容

AI魔术上演前夕,国产存储早已强势清场

脑极体

AI

《财富》杂志公布2024年《财富》全球500强

财见

3D摄影棚布光软件:Set A Light 3D Studio for Mac

你的猪会飞吗

mac软件下载 Mac破解软件

英特尔最新车载独立显卡,汽车装上了AI超级大脑

E科讯

非凸科技入选 IDC中国FinTech Emerging50榜单

非凸科技

FinTech 50 IDC中国金融科技榜单

飞机机翼弹性不足、桥梁晃动频率过高,重要的流固耦合FSI如何用技术解决

Altair RapidMiner

设计 仿真 altair 人工智能、 飞机

解析 Aethir 的代币经济学,如何成为其 DePIN 计算体系的驱动力?

股市老人

1688商品详情API返回值:商品质量监控的利器

技术冰糖葫芦

api 货币化 API 接口 API 文档 API 测试

一个难忘的json反序列化问题

不在线第一只蜗牛

json 反序列

12 个开源社区喊你跟通义灵码结伴编码,赢开源先锋大奖!

阿里巴巴云原生

阿里云 云原生 通义灵码

蓝易云 - Mysql创建用户并且给指定用户添加某个库的所有权限

百度搜索:蓝易云

MySQL 云计算 运维 云服务器 高防服务器

蓝易云 - 跨境电商怎么选择海外服务器

百度搜索:蓝易云

跨境电商 独立站 香港服务器 海外服务器 高防服务器

裁员失业后的自救指南

老张

副业 裁员 第二曲线

提升商品质量监控:1688平台API的深度解析与应用

代码忍者

新知识get,vue3是如何实现在style中使用响应式变量?

快乐非自愿限量之名

Vue 前端开发

探索顶级云文档管理系统:9个最佳选择

爱吃小舅的鱼

文档管理 文档管理工具 文档管理系统 云文档管理系统

重塑购车体验,实时云渲染赋能东风日产探路云看车新体验

3DCAT实时渲染

实时云渲染 云3D渲染 汽车虚拟仿真 云看车

Go语言中如何连接 MySQL,基础必备!

左诗右码

Go

蓝易云 - TDengine函数大全-目录

百度搜索:蓝易云

云计算 tdengine 物联网 云服务器 高防服务器

“AI+Security”系列第2期(一):对抗!大模型自身安全的攻防博弈

云起无垠

需求池管理工具2024:最佳8款推荐

爱吃小舅的鱼

需求管理 需求管理工具

Deep-Live-Cam:只需单张图像即可实现人脸替换;零一万物、月之暗面再掀国产大模型资本战丨 RTE 开发者日报

声网

Gather:开启绝密社交和收益双重惊喜之旅

股市老人

Java 中的泛型 集合(List,Set) Map

快乐非自愿限量之名

Java map 开发语言

手把手教你把PPT压缩到20M以内,这4个技巧办公必备!

职场工具箱

效率 职场 PPT 办公软件 AI生成PPT

深中通道元宇宙启航!3DCAT实时云渲染助力沉浸式体验深中通道

3DCAT实时渲染

元宇宙 元宇宙解决方案 元宇宙文旅

生成式AI及其对API和软件开发的影响

幂简集成

AI API 生成式AI

蓝易云 - 跨境独立站如何选择服务器

百度搜索:蓝易云

云计算 服务器 云服务器 高防服务器 跨境独立站

蓝易云 - 关于Java中时间转化的util小工具

百度搜索:蓝易云

Java 云计算 API 云服务器 香港服务器

面向 RAG 应用开发者的实用指南和建议

Zilliz

人工智能 AI 向量数据库 大语言模型 rag

淘宝店铺商品API返回值中的商品库存与销量信息

技术冰糖葫芦

api 货币化 API 接口 API 文档 API 测试

Redis集群分布式锁与API网关分布式限流
_数据库_颜诗超_InfoQ精选文章