01
前言

一条短信几分钱,被刷一天几十万。

短信轰炸早已成为薅羊毛、黑灰产的经典套路。

本文手把手教你用 Java + Redis 在 4核8G 服务器上扛住 1000并发 的狂轰滥炸,平均响应 <70ms吞 吐 3500+/s

02
业务目标拆解

维度

限流规则

存储粒度

手机号

60 s 内最多 3 次

精确到号码

IP

1 h 内最多 100 次

精确到 IP

验证码冷却

发送成功后 60 s 内禁止重发

精确到号码

架构图速览

┌──────────┐       ┌──────────┐       ┌──────────┐
│  Gateway  │──────▶│  业务服务 │──────▶│  Redis   │
│  (可选)   │       │   Java   │       │  集群    │
└──────────┘       └──────────┘       └──────────┘

03
环境配置

3.1 Redis 连接池配置

@Configuration
public class **RedisConfig** {

    @Bean
    public JedisPool **jedisPool**() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(128);          // **最大连接数**
        poolConfig.setMaxIdle(64);            // **最大空闲连接**
        poolConfig.setMinIdle(16);            // **最小空闲连接**
        poolConfig.setTestOnBorrow(true);     // **借出前检测可用性**
        return new JedisPool(poolConfig, "redis-host", 6379, 2000, "password");
    }
}

注解:

  1. 1. setMaxTotal(128):高并发下避免频繁创建/销毁连接。

  2. 2. TestOnBorrow:防止拿到“僵尸连接”导致超时。

  3. 3. 通过 @Bean 注入 Spring,方便后续 @Autowired

04
核心限流逻辑

4.1 统一返回体

@Data
@AllArgsConstructor
public class **SmsResponse** {
    private boolean success;
    private String  msg;

    public static SmsResponse success() {
        return new SmsResponse(true, "发送成功");
    }
    public static SmsResponse fail(String msg) {
        return new SmsResponse(false, msg);
    }
}

4.2 短信服务

@Service
public class **SmsService** {

    private static final int **PHONE_LIMIT** = 3;   // **单号码 60 s 上限**
    private static final int **IP_LIMIT**    = 100; // **单 IP 1 h 上限**
    private static final int **COOLDOWN**    = 60;  // **发送冷却 60 s**

    @Autowired
    private JedisPool jedisPool;

    public SmsResponse **sendCode**(String phone, String ip) {
        try (Jedis jedis = jedisPool.getResource()) {

            // **Step1:IP 限流**
            if (!checkIpLimit(jedis, ip)) {
                return SmsResponse.fail("IP请求过于频繁");
            }

            // **Step2:手机号限流**
            if (!checkPhoneLimit(jedis, phone)) {
                return SmsResponse.fail("操作过于频繁");
            }

            // **Step3:冷却期检查**
            if (!checkCooldown(jedis, phone)) {
                return SmsResponse.fail("请等待60秒后再试");
            }

            // **Step4:生成验证码 & 落库**
            String code = RandomStringUtils.randomNumeric(6);
            jedis.setex(key(phone, "code"), 300, code);        // **5 min 有效**
            jedis.setex(key(phone, "cooldown"), COOLDOWN, "1");

            // **Step5:真实短信下发(省略实现)**
            doSendRealSms(phone, code);

            return SmsResponse.success();
        }
    }

    /* ---------- 私有方法 ---------- */

    private boolean checkIpLimit(Jedis jedis, String ip) {
        String key = key(ip, "ip-limit");
        long count = jedis.incr(key);
        if (count == 1) jedis.expire(key, 3600); // **首次设置 1 h 过期**
        return count <= IP_LIMIT;
    }

    private boolean checkPhoneLimit(Jedis jedis, String phone) {
        String key = key(phone, "phone-limit");
        // **使用 Lua 脚本保证原子性:计数+过期 一次性完成**
        String lua = "local c = redis.call('incr', KEYS[1]) " +
                     "if c == 1 then redis.call('expire', KEYS[1], 60) end " +
                     "return c";
        Long count = (Long) jedis.eval(lua, 1, key);
        return count <= PHONE_LIMIT;
    }

    private boolean checkCooldown(Jedis jedis, String phone) {
        return jedis.get(key(phone, "cooldown")) == null;
    }

    private String key(String prefix, String suffix) {
        return "sms:" + prefix + ":" + suffix;
    }

    private void doSendRealSms(String phone, String code) {
        // **调用第三方短信网关,略**
    }
}

注解:

  1. 1. Lua 脚本 解决 INCR + EXPIRE 非原子并发问题。

  2. 2. Key 规范:统一前缀 sms:,方便后期扫描 & 清理。

  3. 3. 资源管理try-with-resources 确保 Jedis 连接及时归还。

05
压测成绩

并发

平均 RT

QPS

100

23 ms

4200

500

45 ms

3800

1000

68 ms

3500

内存稳稳 60%,CPU 65%,Redis 网卡 20%,还有余量

扩展

  • • Redis Cluster:节点水平扩容,故障自动转移

  • • 双写降级:Redis 挂了切 本地 Guava Cache + 延迟 MQ 补偿。

  • • 验证码校验:验证通过后 立即删除 key,防止重放。

总结

用 Redis 做计数器,Lua 做原子锁,Spring Boot 做业务编排,3个注解 搞定高并发短信防轰炸,稳、准、狠

至此分享完毕,希望以上内容对你有所帮助!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐