redis-短信手机号码频次限制

发送短信时要对手机号码做频次限制

第一版设计为一分钟发两次,超过两次加黑名单三分钟,加黑名单逻辑会存在分布式锁的问题。

第二版优化为超过限制后直接落错误表,不发送即可。

维度:模板 业务类型  子业务类型 ALL

1.lua 

     返回key对应的当前值,如果是第一次发送,设置过期时间,lua脚本会保证事务性。

 local times = redis.call('incr',KEYS[1])
 if times == 1
 then redis.call('expire',KEYS[1], ARGV[1])
 end
 return times

2.脚本加载

package com.bestpay.messagecenter.product.core.redis.impl;

import com.bestpay.messagecenter.product.common.util.StreamUtil;
import com.bestpay.messagecenter.product.core.model.oss.FreqFilterBO;
import com.bestpay.messagecenter.product.core.redis.FreqRedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 通用频次计数器
 *
 * @author linxing
 * @version Id: FreqRedisServiceImpl.java, v 0.1 2017/6/8 16:20 lxn Exp $$
 */
@Slf4j
@Service
public class FreqRedisServiceImpl implements FreqRedisService {

    /**
     * lua 脚本
     */
    private String scriptShal;

    /**
     * Jedis连接操作
     */
    @Resource
    private JedisConnectionFactory jedisConnectionFactory;


    /**
     * 启动载入lua脚本到redis
     */
    @PostConstruct
    public void loadScript() {
        JedisConnection connection = jedisConnectionFactory.getConnection();
        Jedis jedis = connection.getNativeConnection();
        String script = StreamUtil.convertStreamToString(FreqRedisServiceImpl.class.getClassLoader().
                getResourceAsStream("freqRedisCounter.lua"));
        this.scriptShal = jedis.scriptLoad(script);
        log.info("频率计数器脚本载入成功,sha1:{}", this.scriptShal);
        connection.close();
    }

    public Long getLockTimeForDiffKeyAndSameLimitTime(Map<String, Long> redisKeysMap, Map<String, List<FreqFilterBO>> freqFilterMap) {
        JedisConnection connection = jedisConnectionFactory.getConnection();
        Jedis jedis = connection.getNativeConnection();
        Long lockTime = 0L;
        for (Map.Entry<String, Long> eachKey : redisKeysMap.entrySet()) {
            String redisKey = eachKey.getKey();
            String limitTime = eachKey.getValue().toString();
            List<String> keys = new ArrayList<>();
            List<String> values = new ArrayList<>();
            keys.add(redisKey);
            values.add(limitTime);
            Object evalResult = jedis.evalsha(scriptShal, keys, values);
            Long currentCount = Long.parseLong(evalResult.toString());
            log.info("redis key:{},当前值为:{}",redisKey,currentCount);
            List<FreqFilterBO> freqFilterBOS = freqFilterMap.get(redisKey);
            for (FreqFilterBO eachFilter : freqFilterBOS) {
                if (currentCount > eachFilter.getLimitCount()) {
                    //这里1L代表任意一个大于0,要进行过滤的场景,没有实际意义
                    lockTime = 1L;
                }
            }
        }
        connection.close();
        return lockTime;
    }
}


3.计算锁定时间

/**
     * 计算最大锁定时间
     *
     * @param bos
     * @return
     */
    public Long getBlackListTimeMax(List<FreqFilterBO> bos, String phone) {
        if (CollectionUtils.isEmpty(bos)) {
            log.info("手机号:{},没有找到过滤规则。",phone);
            return -1L;
        }
        Long lockTime;
        Map<String, Long> redisKeyMap = new HashMap<>();
        Map<String, List<FreqFilterBO>> freqFilterMap = new HashMap<>();

        for (FreqFilterBO each : bos) {
            String redisKey = RedisProductKeys.getRecvFreqCounter(phone, each.getRelatedItems(),
                each.getRelatedValue(), each.getLimitTime().toString());
            redisKeyMap.put(redisKey, each.getLimitTime() * 60);
            List<FreqFilterBO> redisKeyFilter = freqFilterMap.get(redisKey);
            if (null == redisKeyFilter) {
                redisKeyFilter = new ArrayList<>();
            }
            redisKeyFilter.add(each);
            freqFilterMap.put(redisKey, redisKeyFilter);
        }
        lockTime = freqRedisService.getLockTimeForDiffKeyAndSameLimitTime(redisKeyMap,
            freqFilterMap);
        redisKeyMap.clear();
        freqFilterMap.clear();
        return lockTime;
    }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值