多AWS实例中Caffeine缓存失效方法及Beanstalk应用缓存更新问询
嘿,针对你提出的这两个关于多实例环境下Caffeine缓存失效的问题,我给你整理几个在AWS生态下实用的解决方案,都是生产环境验证过的:
不管你用的是哪种AWS实例部署方式,这几个方案都能解决跨实例的缓存失效问题:
基于SQS的广播通知
这是AWS生态里最稳妥的方案。当需要失效缓存时,发送一条包含缓存键的消息到SQS队列,所有部署的实例都监听这个队列,收到消息后调用Caffeine的invalidate(key)方法删除对应缓存。
举个Java代码片段参考:// 数据变更时发送失效消息的逻辑 SqsClient sqsClient = SqsClient.create(); sqsClient.sendMessage(SendMessageRequest.builder() .queueUrl("your-cache-invalidation-queue") .messageBody("{\"cacheKey\":\"user:123\"}") .build()); // 实例端监听SQS的处理逻辑(比如用Spring Cloud AWS的注解) @SqsListener("your-cache-invalidation-queue") public void invalidateCache(String messageBody) { ObjectMapper objectMapper = new ObjectMapper(); Map<String, String> msg = objectMapper.readValue(messageBody, Map.class); String cacheKey = msg.get("cacheKey"); yourCaffeineCache.invalidate(cacheKey); }优点是可靠性高,SQS会保证消息至少被消费一次;缺点是有轻微延迟,但对于大多数业务场景完全可以接受。
基于Redis发布/订阅的实时通知
如果你想要更低延迟的失效,可以用Redis的Pub/Sub功能。当数据变更时,发布一条包含缓存键的消息到指定频道,所有实例订阅这个频道,收到消息后立即执行缓存失效。
代码示例:// 发布端(数据变更时) RedisTemplate<String, String> redisTemplate = new RedisTemplate<>(); redisTemplate.convertAndSend("cache-invalidation-channel", "user:123"); // 订阅端(每个实例启动时初始化订阅) @Bean public RedisMessageListenerContainer redisListenerContainer(RedisConnectionFactory connectionFactory) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.addMessageListener((message, pattern) -> { String cacheKey = new String(message.getBody()); yourCaffeineCache.invalidate(cacheKey); }, new PatternTopic("cache-invalidation-channel")); return container; }注意Redis Pub/Sub是“即发即失”的,如果实例刚好离线会错过消息,但结合你设置的15分钟TTL,这个问题会被自动兜底,不会导致长期的数据不一致。
基于SSM Parameter Store的轮询触发
要是你不想引入MQ或Redis这类中间件,可以用AWS Systems Manager Parameter Store。当需要失效缓存时,更新一个特定的SSM参数(比如存储需要失效的缓存键列表,或者一个递增的版本号),然后每个实例定期轮询这个参数,发现变化后执行失效操作。
这种方式适合小型部署场景,轮询频率可以设为1分钟左右,结合TTL也能保证最终一致性。
因为你的应用部署在Beanstalk,数据存在DynamoDB,而且变更频率低,我们可以结合DynamoDB的特性来实现自动化的缓存失效:
利用DynamoDB Streams来自动捕获数据变更事件,然后触发缓存失效流程,具体步骤如下:
- 开启DynamoDB流:在DynamoDB控制台找到你的表,进入「流」标签页,开启流功能,选择合适的捕获模式(比如“新图像和旧图像”,方便你获取变更前后的数据)。
- 创建Lambda函数处理流事件:编写一个Lambda函数订阅这个DynamoDB Stream,当收到数据变更事件时,提取出对应的主键(也就是你缓存时使用的key),然后发送失效消息到SQS或者Redis频道。
Lambda代码示例(Java):
public class DynamoDBStreamHandler implements RequestHandler<DynamodbEvent, Void> {
private final SqsClient sqsClient = SqsClient.create();
private static final String QUEUE_URL = "your-cache-invalidation-queue";
@Override public Void handleRequest(DynamodbEvent event, Context context) { for (DynamodbStreamRecord record : event.getRecords()) { // 假设你的DynamoDB表主键是userId,提取它作为缓存键的一部分 String userId = record.getDynamodb().getKeys().get("userId").getS(); String cacheKey = "user:" + userId; // 发送失效消息到SQS sqsClient.sendMessage(SendMessageRequest.builder() .queueUrl(QUEUE_URL) .messageBody(cacheKey) .build()); } return null; }
}
3. **Beanstalk实例监听失效消息**:在你的Beanstalk应用中添加SQS监听逻辑,收到消息后调用Caffeine的`invalidate(key)`方法删除对应缓存。 另外,因为你已经给Caffeine缓存设置了15分钟的TTL,就算某一次失效通知没被某个实例收到,最多15分钟后缓存也会自动过期,完美实现最终一致性,不用太担心消息丢失的问题。 内容的提问来源于stack exchange,提问作者Onkar Kumbhar

