關於spring boot使用redis的increment()方法自增問題


需求是限制IP頻繁訪問某接口,用的方案是使用redis記錄訪問IP的值,先設定好初始值,每次訪問自增,達到某限定值后,進行阻止。

用的是自定義工具類,使用spring封裝的spring-data-redis進行操作,在對某key進行increment()方法時,報錯:

redis ERR value is not an integer or out of range 

代碼邏輯如下:

 
        
Integer count = (Integer) redisUtil.get(ipAddress);//取得key的value
        if (count == null){
            redisUtil.set(ipAddress,1,10);
            return false;
        }else if(count == 3){
            return false;
        }else {
            redisUtil.incr(ipAddress,1);
            return false;
        }
 
        

 

 
        

 第一次進來,如果沒有redis中沒有數據,則設置key,value和time,key是ip, value初始值為1,有效時長為10秒。

如果沒達到限制次數,則對key自增1。

redisUtil.incr()方法實現如下:

 
        
@Resource
    private RedisTemplate<String, Object> redisTemplate; //這里使用的是redisTemplate

    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

   /**
     * 遞增
     * @param key 鍵
//     * @param by 要增加幾(大於0)
     * @return
     */
    public long incr(String key, long delta){
        if(delta<0){
            throw new RuntimeException("遞增因子必須大於0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }
 
        

 

 
        

開始以為是incr方法接受的參數是long型,但我傳入的是INTEGER類型,但轉換后還是沒有解決問題,問題不是出在這,后來通過查找資料發現,Spring對Redis序列化的策略有兩種,分別是StringRedisTemplate和RedisTemplate,其中StringRedisTemplate用於操作字符串,RedisTemplate使用的是JDK默認的二進制序列化。

大家都知道redis序列化是將key,value值先轉換為流的形式,再存儲到redis中。

RedisTemplate是使用的JdkSerializationRedisSerializer序列化,序列化后的值包含了對象信息,版本號,類信息等,是一串字符串,所以無法進行數值自增操作。

而StringRedisTemplate序列化策略是字符串的值直接轉為字節數組,所以存儲到redis中是數值,所以可以進行自增操作。

StringRedisSerializer源碼:

 
        
public class StringRedisSerializer implements RedisSerializer<String> {
    private final Charset charset;

    public StringRedisSerializer() {
        this(StandardCharsets.UTF_8);
    }

    public StringRedisSerializer(Charset charset) {
        Assert.notNull(charset, "Charset must not be null!");
        this.charset = charset;
    }

    public String deserialize(@Nullable byte[] bytes) {
        return bytes == null ? null : new String(bytes, this.charset);
    }

    public byte[] serialize(@Nullable String string) {
        return string == null ? null : string.getBytes(this.charset); //注意這里是字節數組
    }
}
 
        

 

 
        

所以問題出在這里,我們需要自定義序列化策略,在application啟動類中添加如下:

 
        
 @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        //定義key序列化方式
        //RedisSerializer<String> redisSerializer = new StringRedisSerializer();//Long類型會出現異常信息;需要我們上面的自定義key生成策略,一般沒必要
        //定義value的序列化方式
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // template.setKeySerializer(redisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
 
        

費了2多小時才成功解決問題,RedisUtil.incr()能夠成功對key進行自增了,如有錯誤之處請歡迎指出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM