使用Caffeine缓存Springboot缓存Kafka记录

时间:2020-02-01 12:51:00

标签: spring-boot kafka-consumer-api spring-kafka caffeine caffeine-cache

我正在尝试在间隔3分钟后缓存Kafka记录,因为它将过期并从缓存中删除。

使用springboot编写的使用kafka消费者获取的每个传入记录都需要先在缓存中进行更新,然后如果存在,我需要丢弃下一个重复记录(如果它与缓存记录匹配)。

我尝试过如下使用咖啡因缓存,

@EnableCaching
public class AppCacheManagerConfig {

    @Bean
    public CacheManager cacheManager(Ticker ticker) {
        CaffeineCache bookCache = buildCache("declineRecords", ticker, 3);
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(Collections.singletonList(bookCache));
        return cacheManager;
    }

    private CaffeineCache buildCache(String name, Ticker ticker, int minutesToExpire) {
        return new CaffeineCache(name, Caffeine.newBuilder().expireAfterWrite(minutesToExpire, TimeUnit.MINUTES)
                .maximumSize(100).ticker(ticker).build());
    }

    @Bean
    public Ticker ticker() {
        return Ticker.systemTicker();
    }

}

我的卡夫卡消费者如下所示,

@Autowired
    CachingServiceImpl cachingService;

@KafkaListener(topics = "#{'${spring.kafka.consumer.topic}'}", concurrency = "#{'${spring.kafka.consumer.concurrentConsumers}'}", errorHandler = "#{'${spring.kafka.consumer.errorHandler}'}")
    public void consume(Message<?> message, Acknowledgment acknowledgment,
            @Header(KafkaHeaders.RECEIVED_TIMESTAMP) long createTime) {
        logger.info("Recieved Message: " + message.getPayload());
        try {
            boolean approveTopic = false;
            boolean duplicateRecord = false;
if (cachingService.isDuplicateCheck(declineRecord)) {
//do something with records
}
else
{
//do something with records
}
    cachingService.putInCache(xmlJSONObj, declineRecord, time);

我的缓存服务如下,

@Component
public class CachingServiceImpl {
    private static final Logger logger = LoggerFactory.getLogger(CachingServiceImpl.class);
    @Autowired
    CacheManager cacheManager;

    @Cacheable(value = "declineRecords", key = "#declineRecord", sync = true)
    public String putInCache(JSONObject xmlJSONObj, String declineRecord, String time) {
        logger.info("Record is Cached for 3 minutes interval check", declineRecord);
        cacheManager.getCache("declineRecords").put(declineRecord, time);
        return declineRecord;

    }

    public boolean isDuplicateCheck(String declineRecord) {
        if (null != cacheManager.getCache("declineRecords").get(declineRecord)) {
            return true;
        }
        return false;
    }
}

但是,每当有一条记录进入使用者时,我的缓存始终为空。它没有保存记录。

所做的修改:

在完成建议后,我已经添加了以下配置文件,并且更多的R&D删除了一些较早的逻辑,现在缓存可以按预期工作,但是当所有三个使用者都发送相同的记录时,重复检查失败。

`

  @Configuration
  public class AppCacheManagerConfig {
  public static Cache<String, Object> jsonCache = 
  Caffeine.newBuilder().expireAfterWrite(3, TimeUnit.MINUTES)
            .maximumSize(10000).recordStats().build();
    @Bean
    public CacheLoader<Object, Object> cacheLoader() {
        CacheLoader<Object, Object> cacheLoader = new CacheLoader<Object, Object>() {
            @Override
            public Object load(Object key) throws Exception {
                return null;
            }

            @Override
            public Object reload(Object key, Object oldValue) throws Exception {
                return oldValue;
            }
        };
        return cacheLoader;
    }

`

现在我将上述缓存用作手动放置和获取。

1 个答案:

答案 0 :(得分:1)

我猜您正在尝试为Kafka实施记录重复数据删除。

这是类似的讨论:

https://github.com/spring-projects/spring-kafka/issues/80

这是当前的抽象类,您可以对其进行扩展以实现必要的结果:

https://github.com/spring-projects/spring-kafka/blob/master/spring-kafka/src/main/java/org/springframework/kafka/listener/adapter/AbstractFilteringMessageListener.java

您的缓存服务绝对是不正确的:Cacheable批注允许标记数据获取器和设置器,以通过AOP添加缓存。在代码中,您可以清楚地实现一些自己的低级缓存更新逻辑。

至少下一次可能的更改可能会帮助您:

  1. 删除@Cacheable。您不需要它是因为您手动使用缓存,因此它可能是冲突的根源(尤其是在使用sync = true时)。如果有帮助,也可以删除@EnableCaching-它使您支持此处不需要的与缓存相关的Spring注释。

  2. 尝试使用适用于其他bean的参数删除Ticker bean。它不会对您的配置有害,但是通常它仅对测试有用,而无需另外定义。

  3. 仔细检查什么是declineRecord。如果它是序列化的对象,请确保序列化正常进行。

  4. 添加recordStats()进行缓存,并输出stats()进行日志记录以进行进一步分析。