Redis 7 的分片发布/订阅总结
这是2022年AWS游戏附属日历的第四天文章。
Redis 提供了实现游戏工作负载所需组件的便利功能,例如排行榜、消息传递、缓存等。本文将重点讨论消息传递,并介绍 Redis 7 新增的 Sharded Pub/Sub 在游戏中客户端通知的使用示例。
客户通知的使用案例和Redis Pub / Sub
成為配對、大廳進出、成就顯示、聊天發布等服務器端或其他玩家狀態變化的案例可以即時通知到客戶端。實現客戶端通知有幾種方式,但WebSocket和Redis Pub/Sub的組合可能是許多人熟悉的方法。在這裡,我們回顧了Redis Pub/Sub,並總結了Redis 7新增的分片式Pub/Sub功能。
Redis Pub/Sub的概述
在游戏中,可以利用以下的方式实现类似的玩家体验。假设在多人游戏中,您正在大厅等待一起战斗的队友。当有人进入大厅时,会伴随着音效显示类似于“〇〇进入了房间”的消息在屏幕边缘。如果这是用Redis Pub/Sub来实现的,那么在同一个大厅中的玩家(客户端)可以订阅同一个频道,并在进入时发布消息到该频道,就可以实现这种表现。
正确的说法是,不是“游戏客户端(控制台、移动设备、个人电脑等)= Redis客户端”,而是由Node.js等创建的WebSocket服务器扮演着这个角色。
Redis Pub/Sub 的问题是什么?
Redis Pub/Sub的好处是可以通过”SUBSCRIBE”和”PUBLISH”等操作来简单实现客户端通知的后端,但实际上也存在一些问题。
由于发布的消息通过集群总线广播到所有节点,所以节点数量越多,广播就会使用更多的带宽。尽管增加节点是为了扩展集群,但相反会导致整体吞吐量下降。
在考虑操作大规模的Redis集群时需要注意,如果无法忽视吞吐量下降的影响,则需要采取一些解决方法,如拆分复制组或使用Redis Stream(Redis 5之后)来替代Redis Pub/Sub的实现。
顺便提一下,Pub/Sub消息的广播无法一次处理完时,也存在集群总线的缓冲管理机制的bug,会消耗大量的CPU/内存资源。截止本文撰写时,最新稳定版Redis 7.0.5还没有合并该修复。关于这一点,ElastiCache for Redis的文档也提醒了注意。
在启用了集群模式的集群中,已发布的消息会通过集群总线广播到其他所有节点。由于集群总线的缓冲管理,当Pub/Sub流量增加时,可能会导致EngineCPUUtilization增加。
分片式发布/订阅
Sharded Pub/Sub 是 Redis 7中新增的新的Pub/Sub功能。在集群模式下,传统的Pub/Sub会将发布的消息广播到集群内的所有节点,而Sharded Pub/Sub只会将消息广播到分片内的节点。因此,通过集群总线传输的数据量比以前减少了。在Redis的官方文档中,过去的Pub/Sub被称为“Global Pub/Sub”,以与Sharded Pub/Sub区分。所以,在这里我也将使用“Global Pub/Sub”。
顺便提一句,截至2022年11月8日,ElastiCache for Redis已支持Redis 7版本。既然机会难得,我想试一试在ElastiCache for Redis上使用分片式发布/订阅。
尝试使用Sharded Pub/Sub
创建ElastiCache for Redis集群
-
- CLI で作成していきます.
-
- サブネット, セキュリティグループは既存のものを使うので割愛.
- コマンド実行結果も割愛
创建子网组。
aws elasticache create-cache-subnet-group \
--cache-subnet-group-name redis-subnet-grp \
--cache-subnet-group-description "Testing" \
--subnet-ids [subnet-id-1a] [subnet-id-1b]
创建集群
create-replication-group を使うとクラスターモード有効となります.
–num-node-groups でシャード数を指定します.
aws elasticache create-replication-group \
--replication-group-id my-redis7-repl-grp \
--replication-group-description "Cluster mode enabled" \
--multi-az-enabled \
--num-node-groups 2 \
--replicas-per-node-group 1 \
--cache-node-type cache.r6g.large \
--engine redis \
--engine-version 7.0 \
--cache-parameter-group-name default.redis7.cluster.on \
--cache-subnet-group-name redis-subnet-grp
3. 设定安全组
- なぜだか, クラスタ作成時にセキュリティグループの設定がうまくできなかったので, 別途実施
aws elasticache modify-replication-group \
--replication-group-id my-redis7-repl-grp \
--security-group-ids [sg-id]
到目前为止,Redis集群的准备工作已经完成。
快來吧,Sharded Pub/Sub!
首先,从与ElastiCache集群相同的VPC中的EC2实例使用redis-cli连接到配置端点。在这里,我们准备了两个客户端,并分别执行SSUBSCRIBE和SPUBLISH命令。
my-redis7-repl-grp.aaa000.clustercfg.usw2.cache.amazonaws.com:6379>
[ec2-user@ip-10-0-13-148 ~]$ redis-cli -c -h my-redis7-repl-grp.aaa000.clustercfg.usw2.cache.amazonaws.com
SSUBSCRIBE コマンドでチャネルをサブスクライブします. ここでは, “lobby_001” というチャンネルをサブスクライブしています.
my-redis7-repl-grp.aaa000.clustercfg.usw2.cache.amazonaws.com:6379> SSUBSCRIBE lobby_001
Reading messages... (press Ctrl-C to quit)
1) "ssubscribe"
2) "lobby_001"
3) (integer) 1
现在,在另一个客户端上使用SPUBLISH命令发送消息。
my-redis7-repl-grp.aaa000.clustercfg.usw2.cache.amazonaws.com:6379> spublish lobby_001 'Alice is here!'
(integer) 0
my-redis7-repl-grp.aaa000.clustercfg.usw2.cache.amazonaws.com:6379> spublish lobby_001 'Bob is here!'
(integer) 0
my-redis7-repl-grp.aaa000.clustercfg.usw2.cache.amazonaws.com:6379> spublish lobby_001 'Alice left...'
(integer) 0
- パブリッシュしたメッセージは即時にサブスクライバーに届きます.
my-redis7-repl-grp.aaa000.clustercfg.usw2.cache.amazonaws.com:6379> ssubscribe lobby_001
Reading messages... (press Ctrl-C to quit)
1) "ssubscribe"
2) "lobby_001"
3) (integer) 1
1) "smessage"
2) "lobby_001"
3) "Alice is here!"
1) "smessage"
2) "lobby_001"
3) "Bob is here!"
1) "smessage"
2) "lobby_001"
3) "Alice left..."
基本使用方法和行为似乎与以前的Pub/Sub没有太大变化。
除此之外,还提供了其他一些子命令,如SUNSUBSCRIBE命令、PUBSUB SHARDCHANNELS、PUBSUB SHARDNUMSUB等,它们分别具有与全局Pub/Sub的UNSUBSCRIBE、PUBSUB CHANNELS、PUBSUB NUMSUB相对应的功能。
然而,正如其名“Sharded”所示,Sharded Pub/Sub不跨越分片进行共享,因此与之前的Pub/Sub相比也有一些不同之处。此外,Sharded Pub/Sub也会根据通常的键哈希槽映射算法来将分片分散到节点上。
当客户端将消息发送到“lobby_001”时,如果它要将消息发送到“lobby_002”,系统会将其重定向到映射了“lobby_002”的分片上。这是因为“lobby_001”和“lobby_002”被分配到不同的分片上。
my-redis7-repl-grp.aaa000.clustercfg.usw2.cache.amazonaws.com:6379> spublish lobby_002 'hello'
→ Redirected to slot [7392] located at 10.0.149.64:6379
(integer) 0
10.0.149.64:6379>
当您尝试通过SSUBSCRIBE订阅不同分片的频道时,会出现CROSSSLOT Keys错误。
my-redis7-repl-grp.aaa000.clustercfg.usw2.cache.amazonaws.com:6379> ssubscribe lobby_001 lobby_002
Reading messages... (press Ctrl-C to quit)
(error) CROSSSLOT Keys in request don't hash to the same slot
目前还没有实现与Global Pub/Sub的模式匹配订阅(PSUBSCRIBE)相对应的命令,但未来可能会实现。
分片的发布-订阅实现 #8621
目前尚未实现PSUBSCRIBELOCAL(模式订阅),但没有技术性障碍。动机在于行为不够明显,无法确定哪个模式应该与哪个插槽关联。暂时决定忽略它。
总结
通过对 Pub/Sub 进行分片,可以采用 Sharded Pub/Sub 方法来减少通过集群总线传输的数据量,从而希望能够构建大规模可扩展的 Pub/Sub 系统。与不需要考虑分片的 Global Pub/Sub 相比,显然在实现方面还是需要考虑一些因素的。让我们继续关注 Sharded Pub/Sub 今后的更新。原先误将 Sharded Pub/Sub 看作 Shared Pub/Sub,并以为是可以在集群模式下使用的共享 Pub/Sub!对此进行了研究后,结果变成了这种输出形式,真是太好了。
请参考提供的资料。
-
- Redis ドキュメント, GitHub Issues
Sharded Pub/Sub
Redis Cluster PubSub – Scalability Issues #2672
Sharded pubsub implementation #8621
Understanding Pub/Sub Clustering Architecture #1927
[BUG] Clusterbus buffer management can consume significant memory and CPU utilization during pubsub #10863
Re-design cluster link send buffer to improve memory management #11343
AWS ドキュメント, ブログなど
Common ElastiCache Use Cases and How ElastiCache Can Help – Messaging (Redis Pub/Sub)
Best practices: Pub/Sub