【已解决】Java 项目中利用 Redis 配合 Lua 脚本对短信推送消息做推送限制

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 【已解决】Java 项目中利用 Redis 配合 Lua 脚本对短信推送消息做推送限制

🎉场景介绍:在实际应用场景中,我们通常需要对短信推送消息做发送限制的处理,避免一定时间间隔内发送过多相同内容的推送短信对用户造成骚扰,也要防止攻击者恶意调用短信推送接口造成短信资源的极大浪费。尽管部分三方接口提供方的原生接口就已经做了限制,但是为了能够更好地满足定制化需求以及在编码过程中的疏忽造成接口重复调用导致资源浪费,因此我们需要在项目中做短信推送限制的处理。

💡方案设计:

首先,将需要推送的短信内容以及接收短信的手机号进行拼接成新的字符串,并对该字符串进行加密处理转换成16进制字符串,作为存入到 redis 中的 key.

其次,执行 Lua 脚本,该脚本用于在 redis 中存储具有过期时间的 key,并返回重复校验的结果。

最后,获取校验结果,根据校验结果进行相应的处理操作,如果是重复发送的短信就将该接口和手机号加入数据埋点,用于监控推送限制情况,并抛出重复发送的异常;否则,正常发送短信消息。

在对项目中的业务代码做了抽离后,只保留了和方案设计相关的代码,如下所示

 public class SmsService {
  private static Logger logger = LoggerFactory.getLogger(SmsService.class);
  @Autowired
    private JedisTemplate jedisTemplate;
  @Autowired
  private MeterRegistry meterRegistry;
    private static final String LIMIT_LUA = "local num = redis.call('incr', KEYS[1])\n" +
            "if num == 1 then\n" +
            "      redis.call('expire', KEYS[1], ARGV[1])\n" +
            "end\n" +
            "if num > tonumber(ARGV[2]) then\n" +
            "      return 0\n" +
            "end\n" +
            "return 1";
  /**
     * 发送短信
     * @param sendSmsRequest 发送短信请求信息
     * @return 
     */
    @Transactional
    public String sendSms(SendSmsRequest sendSmsRequest) {
      String key = sendSmsRequest.getPhoneNumber() + sendSmsRequest.getTemplateCode() + sendSmsRequest.getTemplateParam();
    key = DigestUtils.md5Hex(key);
        // 60秒内同一手机号相同内容只允许发送两次
        boolean limitFlag = checkIsLimited(key, 60L, 2L);
        if (limitFlag) {
            logger.warn("短信推送被限制,sendSmsRequest:{}", JsonTool.serialize(sendSmsRequest));
            // 加入数据埋点,用于监控超出推送限制的情况
            meterRegistry.counter("sms.repeat.limit.count", "/test/sms", uri, sendSmsRequest.getPhoneNumber(), user).increment();
            throw new ServiceException("短信推送超出限制");
        }
    // 推送短信,次数省略100w行代码...
     }
    /**
     * 推送频率限制,seconds内只允许limitNum次
     * @param key key
     * @param seconds 时间 秒
     * @param limitNum 限制次数
     * @return true:限制 false:不限制
     */
    public boolean checkIsLimited(String key, long seconds, long limitNum) {
        try {
            Long result = (Long) jedisTemplate.eval(LIMIT_LUA, CoreTool.array2List(key), CoreTool.array2List(String.valueOf(seconds), String.valueOf(limitNum)));
            return result == 0;
        } catch (Exception e) {
            logger.warn("校验是否需要推送限制异常", e);
        }
          return false;
      }
  }
}

其中,对上述 Lua 脚本功能具体的注释如下:

local num = redis.call('incr', KEYS[1])  -- 计数加1(第一次调用时,key不存在,默认会创建并赋值1)
if num == 1 then                         -- 第一次调用
  redis.call('expire', KEYS[1], ARGV[1])  -- 对key设置过期时间
end
if num > tonumber(ARGV[2]) then          -- 非第一次访问,判断是否在过期时间内大于限定的访问次数
  return 0
end
return 1


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
NoSQL 安全 测试技术
Redis游戏积分排行榜项目中通义灵码的应用实战
Redis游戏积分排行榜项目中通义灵码的应用实战
56 4
|
17天前
|
NoSQL Java 关系型数据库
Liunx部署java项目Tomcat、Redis、Mysql教程
本文详细介绍了如何在 Linux 服务器上安装和配置 Tomcat、MySQL 和 Redis,并部署 Java 项目。通过这些步骤,您可以搭建一个高效稳定的 Java 应用运行环境。希望本文能为您在实际操作中提供有价值的参考。
89 26
|
29天前
|
XML Java 测试技术
从零开始学 Maven:简化 Java 项目的构建与管理
Maven 是一个由 Apache 软件基金会开发的项目管理和构建自动化工具。它主要用在 Java 项目中,但也可以用于其他类型的项目。
46 1
从零开始学 Maven:简化 Java 项目的构建与管理
|
27天前
|
Java
Java项目中高精度数值计算:为何BigDecimal优于Double
在Java项目开发中,涉及金额计算、面积计算等高精度数值操作时,应选择 `BigDecimal` 而非 `Double`。`BigDecimal` 提供任意精度的小数运算、多种舍入模式和良好的可读性,确保计算结果的准确性和可靠性。例如,在金额计算中,`BigDecimal` 可以精确到小数点后两位,而 `Double` 可能因精度问题导致结果不准确。
|
1月前
|
Java Android开发
Eclipse 创建 Java 项目
Eclipse 创建 Java 项目
44 4
|
6天前
|
存储 缓存 NoSQL
解决Redis缓存数据类型丢失问题
解决Redis缓存数据类型丢失问题
124 85
|
2月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
84 6
|
4天前
|
缓存 监控 NoSQL
Redis经典问题:缓存穿透
本文详细探讨了分布式系统和缓存应用中的经典问题——缓存穿透。缓存穿透是指用户请求的数据在缓存和数据库中都不存在,导致大量请求直接落到数据库上,可能引发数据库崩溃或性能下降。文章介绍了几种有效的解决方案,包括接口层增加校验、缓存空值、使用布隆过滤器、优化数据库查询以及加强监控报警机制。通过这些方法,可以有效缓解缓存穿透对系统的影响,提升系统的稳定性和性能。
|
1月前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
1月前
|
存储 缓存 NoSQL
【赵渝强老师】基于Redis的旁路缓存架构
本文介绍了引入缓存后的系统架构,通过缓存可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。文中提供了相关图片和视频讲解,并讨论了数据库读写分离、分库分表等方法来减轻数据库压力。同时,文章也指出了缓存可能带来的复杂度增加、成本提高和数据一致性问题。
【赵渝强老师】基于Redis的旁路缓存架构