【问题标题】:Getting an IllegalArgumentException: Cannot convert String to AtomicInteger when trying to add data that contains an AtomicInteger int redis cache获取 IllegalArgumentException:尝试添加包含 AtomicInteger int redis 缓存的数据时无法将 String 转换为 AtomicInteger
【发布时间】:2022-01-05 09:44:13
【问题描述】:

我正在尝试将数据模型添加到 redis 缓存。当我取出模型时,我得到了异常

org.springframework.core.convert.ConversionFailedException: Failed to convert from type [byte[]] to type [java.util.concurrent.atomic.AtomicInteger] for value '{53}'; nested exception is java.lang.IllegalArgumentException: Cannot convert String [5] to target class [java.util.concurrent.atomic.AtomicInteger]] with root cause

模型是

@RedisHash(value = "ThottleRate", timeToLive = 5)

public class ThottleRate implements Serializable  {
 
       private static final long serialVersionUID = 1L;

       @Id
       private String url;
       private AtomicInteger rate;

       public ThottleRate(String url, int rate) {
             super();
             this.url = url;
             this.rate = new AtomicInteger(rate);
       }

       public boolean isAllowed(){
           if(this.rate.decrementAndGet() <= 0) {
                    return false;
             }
             return true;
       }

调用代码是

try {
                    option = throttleRateRepository.findById(url);
                    ThottleRate rate = option.get();
                    allow = rate.isAllowed();
             } catch(NoSuchElementException e) {
                throttleRateRepository.save(new ThottleRate(url, 5));
                    allow = true;
             }

我想要做的是限制功能,使用 redis 缓存。它有一个存在的时间,并且在此期间可以访问一个 url。

但是当调用是

option = throttleRateRepository.findById(url);

这会引发 IllegalArgumentException

这似乎是使用 redis 缓存的最简单方法,具有生存时间和多个速率

【问题讨论】:

    标签: java spring redis throttling spring-data-redis


    【解决方案1】:

    抛出异常是因为 Spring Data Redis 在加载模型时不知道如何将存储在 Redis 中的字节数组反序列化为 AtomicInteger 类型。您需要为此注册一个数据类型转换器。

    这里是示例数据类型转换器:

    @Component
    @ReadingConverter
    public class BytesToAtomicIntegerConverter implements Converter<byte[], AtomicInteger> {
    
      @Override
      public AtomicInteger convert(byte[] source) {
        if (ObjectUtils.isEmpty(source)) {
          return null;
        }
    
        int n = NumberUtils.parseNumber(
            new String(source, StandardCharsets.UTF_8), Integer.class);
        return new AtomicInteger(n);
      }
    }
    

    可以通过将此 bean 添加到 SpringBoot 配置中来向 Spring Data Redis 注册:

    @Bean
    public RedisCustomConversions redisCustomConversions(
      BytesToAtomicIntegerConverter converter) {
    
      return new RedisCustomConversions(List.of(converter));
    }
    

    一旦注册数据转换器,错误就会消失。

    不过,在实施此类解决方案时,您必须注意一些事项。 AtomicInteger 字段在这里用于记录特定人员的请求数。如果有多个服务器为请求提供服务,那么不同的服务器最终将在本地内存中的该字段中存储不同的值。由于与 Redis 的通信需要时间(即使仅在毫秒范围内),因此该值被另一台服务器更新的可能性远非零。如果这种偏差对于用例来说是可以的,这可能不是问题。

    我还建议查看一些有助于实现请求限制的现有库。下面是一些例子:

    1. Throttling a Rest API in Java
    2. Rate Limiting a Spring API Using Bucket4j
    3. Implementing Throttling in Java (Spring Boot)

    【讨论】:

    • 非常感谢您的回答以及有关节流的更多信息。我确实看过他们。但是我之所以这样做是因为 URL 是一个参数,在一个对象中,测试节流,然后使用 URL 调用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-13
    • 1970-01-01
    • 1970-01-01
    • 2018-04-18
    • 2019-03-27
    相关资源
    最近更新 更多