前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >如何确保Redis Pub/Sub模式的数据安全?

如何确保Redis Pub/Sub模式的数据安全?

作者头像
SmileNicky
发布2024-12-23 11:10:01
发布2024-12-23 11:10:01
9800
代码可运行
举报
文章被收录于专栏:Nicky's blog
运行总次数:0
代码可运行
业务场景

最近遇到一个生产环境问题,排查了比较久,记录一下,方便以后反思。 业务场景:实现每天的考勤打卡提醒,根据配置的规则数据,比如每天提前几分钟,提醒员工打卡,所以会下班前几分钟推送消息到微信公众号,提醒员工,记得打卡考勤

技术实现:会有一个定时任务,每天都会扫描一遍,根据配置的规则,比如提前n分钟提醒考勤打卡,这个过程会计算好需要提前n分钟执行的业务数据,然后放在一个基于Redis发布订阅模式实现的延时队列里,到预定的时间点,延时任务就会执行,发送消息提醒

排查过程

但是项目运行一段时间后,发现收不到消息,所以需要排查具体原因,通过Arthas一步一步定位问题,发现是封装的Redis延时队列组件出问题,丢到队列里的消息,偶尔会收不到,和公司架构师讨论,解决方法是先修改redis的client-output-buffer-limit配置,然后修改封装的延时队列组件,比如加上重试机制,保证不会丢失发布订阅消息

延时队列,基于Redis的Pub/Sub模式实现

代码语言:javascript
代码运行次数:0
复制
package cn.core.common.redis.delayqueue;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPubSub;

@Component
@Slf4j
public class KeyExpiredListener extends JedisPubSub {
  
    @Override
    public void onPSubscribe(String pattern, int subscribedChannels) {
        log.info("onPSubscribe pattern:{} subscribedChannels:{}",pattern,subscribedChannels);
        
    }

    @Override
    public void onPMessage(String pattern, String channel, String message) {
		log.info("onPMessage pattern:{} channel:{} message:{}",pattern,channel,message);
		
    }
}

借助Arthas排查问题,sc -d命令查看一下JVM加载的类信息,比如获取一下Hutool的SpringUtil类的信息,为什么获取这个?因为需要根据这个类来getBean,然后配合ognl命令来调用延时队列组件里的方法

代码语言:javascript
代码运行次数:0
复制
sc -d cn.hutool.extra.spring.SpringUtil

上面命令在Arthas里执行后,会返回 6dc9d846,再用ognl命令,使用表达式来进行动态调用

代码语言:javascript
代码运行次数:0
复制
ognl -c 6dc9d846 '@cn.hutool.extra.spring.SpringUtil@getBean("keyExpiredListener").onPMessage("1","1","test:key:110028")'
原因分析

通过上面的Arthas动态调用测试,发现这个组件,偶尔能收到消息,偶尔收不到消息,所以通过和架构师讨论和网上查询资料,推测可能是网络带宽或者是生产消息过多超多了Redis的Pub/Sub的最大限制

Redis为了避免输出缓冲区消息大量堆积的隐患,设置了一些保护机制:

  • 缓冲区大小限制,对于Pub/Sub客户端,也就是发布/订阅模式,大小限制是8M,当缓冲区超过8M时,会关闭连接
  • 持续性限制,当一个客户端的缓冲区持续一段时间占用太大空间时会关闭连接,发布订阅模式的默认限制是,当客户端缓冲区大小持续60s超过2M时会关闭客户端连接
解决方法

Redis 的 client-output-buffer-limit 参数用于设置客户端输出缓冲区的大小限制,以防止慢速客户端消耗过多的内存资源。

参数格式

代码语言:javascript
代码运行次数:0
复制
   client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
  • class:客户端类型,可以是 normal(普通客户端)、slave(从节点客户端)、pubsub(发布订阅模式下的客户端)。
  • hard limit:硬限制,表示输出缓冲区的最大字节数,一旦超过这个限制,Redis 会立即断开客户端的连接。
  • soft limitsoft seconds:软限制,表示在 soft seconds 指定的时间范围内,如果输出缓冲区的大小超过了 soft limit,则 Redis 会断开客户端的连接。

参数配置

  • 可以在Redis的redis.conf配置
代码语言:javascript
代码运行次数:0
复制
client-output-buffer-limit pubsub 32mb 8mb 60   #当缓冲区数据达到硬限制32M时,连接会关闭;当缓冲区数据达到软限制每60秒8M时,连接也会关闭
client-output-buffer-limit pubsub 0 0 0         #将hard limit和soft limit同时置0,表示关闭该限制。生产环境不推荐 

动态调整客户端的输出缓冲区限制,可以通过 CONFIG SET 命令来修改,可以直接在redis-cli执行命令:

代码语言:javascript
代码运行次数:0
复制
127.0.0.1:0>CONFIG SET client-output-buffer-limit 'pubsub 64mb 32mb 60'
"OK"
127.0.0.1:0>CONFIG GET client-output-buffer-limit
 1)  "client-output-buffer-limit"
 2)  "normal 0 0 0 slave 268435456 67108864 60 pubsub 67108864 33554432 60"

监控慢速的客户端,可以使用 CLIENT LIST 命令来查看各个客户端的状态,在输出中,omem 表示该客户端当前使用的输出缓冲区大小。

代码语言:javascript
代码运行次数:0
复制
127.0.0.1:0> CLIENT LIST
"id=3 addr=127.0.0.1:50097 laddr=127.0.0.1:6379 fd=7 name= age=1590 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 argv-mem=10 obl=0 oll=0 omem=0 tot-mem=49816 events=r cmd=client user=default redir=-1
"

通过这些配置,可以有效地控制不同类型客户端的输出缓冲区大小,确保 Redis 服务器的稳定性和性能。可以根据业务情况先修改redis的client-output-buffer-limit配置,针对这种发布订阅模式,还可以加上重试机制,保证不会丢失发布订阅消息

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 业务场景
  • 排查过程
  • 原因分析
  • 解决方法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档