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 redisKeysMap, Map> freqFilterMap) {
        JedisConnection connection = jedisConnectionFactory.getConnection();
        Jedis jedis = connection.getNativeConnection();
        Long lockTime = 0L;
        for (Map.Entry eachKey : redisKeysMap.entrySet()) {
            String redisKey = eachKey.getKey();
            String limitTime = eachKey.getValue().toString();
            List keys = new ArrayList<>();
            List 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 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 bos, String phone) {
        if (CollectionUtils.isEmpty(bos)) {
            log.info("手机号:{},没有找到过滤规则。",phone);
            return -1L;
        }
        Long lockTime;
        Map redisKeyMap = new HashMap<>();
        Map> 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 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;
    }



你可能感兴趣的:(redis)