【问题标题】:How to acess spring data redis stored object at the expiration event?如何在过期事件中访问spring data redis store对象?
【发布时间】:2020-09-19 04:22:32
【问题描述】:

我使用 Spring Data Redis 将购物车存储在 Redis 中特定时间。使用 @TimeToLive 注释的过期属性设置 Cart 对象的生存时间,如下面的代码所述。 我设置了KeyExpirationEventMessageListener 类型来监听过期事件,以便在过期事件中处理额外的工作。我能够从过期对象的触发事件中获取密钥,并且我试图在过期时使用 spring 数据存储库访问它或其幻像对象,但没有结果。它返回一个空对象,这意味着原始对象对象很可能已被删除。我不知道这是否是正确的方法。但是,有没有办法在到期时或在它被删除以处理移动工作之前获取到期对象?

@RedisHash("cart")
public class Cart implements Serializable {
    @Id
    @Indexed
    private String id;
    private long customerId;
    private Set<CartLine> lines = new HashSet<>();

    @TimeToLive
    private long expiration;

}

public interface ShoppingCartRepository extends CrudRepository<Cart, String> {

}


    @Component
    public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {

        private RedisTemplate<?, ?> redisTemplate;
        private ShoppingCartRepository repository;
        public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer,
                                          RedisTemplate redisTemplate, ShoppingCartRepository repository) {
            super(listenerContainer);
            this.redisTemplate = redisTemplate;
            this.repository = repository;
        }

        @Override
        public void onMessage(Message message, byte[] pattern) {
            String key = new String(message.getBody());
            try {
                String id = extractId(key);
                Optional<ShoppingCart> cart = repository.findById(id);
            } catch(Exception e) {
                logger.info("something went wrong  ====>  " + e.getStackTrace());
            }
        }
        private String extractId(String key){
            String keyPrefix = ShoppingCart.class.getAnnotation(RedisHash.class).value();
            return key.substring((keyPrefix + ":").length());
        }
    }

【问题讨论】:

    标签: spring-boot spring-data spring-data-redis


    【解决方案1】:

    使用 Spring boot 2.3.4.RELEASE 的示例

    @RedisHash("cart")
    @AllArgsConstructor
    @Getter
    @Setter
    public class Cart implements Serializable {
        @Id
        @Indexed
        private String id;
        private String customerName;
        @TimeToLive
        private long expiration;
    }
    
    public interface ShoppingCartRepository extends CrudRepository<Cart, String> {
    }
    
    @SpringBootApplication
    @EnableRedisRepositories(enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP)
    public class RedisExpirationApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(RedisExpirationApplication.class, args);
        }
    
        @Bean
        public CommandLineRunner run(ShoppingCartRepository shoppingCartRepository) {
            return args -> {
                // TTL 1 second
                shoppingCartRepository.save(new Cart("some-id", "My Customer Name", 1));
                // wait 5 seconds
                Thread.sleep(5000L);
            };
        }
    
        @Component
        public class CartListener {
            // need to enable redis notifications, inside redis-cli type:
            // config set notify-keyspace-events KEA
            @EventListener
            public void anything(RedisKeyExpiredEvent<Cart> expiredCart) {
                Cart cart = (Cart) expiredCart.getValue();
                System.out.println(cart.getId());
                System.out.println(cart.getCustomerName());
            }
        }
    }
    

    【讨论】:

    • 感谢 Nilzao,与 Spring Boot 2.3.4 完美集成。这工作得很好。
    • @ROUISSIMohamedALi 您是使用属性enableKeyspaceEvents 在代码中启用键空间事件还是从 Redis 控制台手动启用?
    • @AbhijitSarkar 我是从 Redis 控制台手动完成的。
    【解决方案2】:

    我的场景里曾经有一个用例,其实可以使用RedisKeyExpiredEvent,

    • RedisKeyExpiredEvent [0] 是发布的特定于 Redis 的 ApplicationEvent 当 Redis 中的特定键过期时。它可以保持的价值 密钥旁边的过期密钥。

    您可以继续执行以下操作。

    @SpringBootApplication
    @EnableRedisRepositories(considerNestedRepositories = true, enableKeyspaceEvents = EnableKeyspaceEvents.ON_STARTUP)
    static class Config {
    
        /**
         * An {@link ApplicationListener} that captures {@link RedisKeyExpiredEvent}s and just prints the value to the
         * console.
         *
         * @return
         */
        @Bean
        ApplicationListener<RedisKeyExpiredEvent<Person>> eventListener() {
            return event -> {
                System.out.println(String.format("Received expire event for key=%s with value %s.",
                        new String(event.getSource()), event.getValue()));
            };
        }
    }
    

    您可以在 [1] 找到示例实现。

    【讨论】:

    • 我在起始类的顶部添加了@EnableRedisRepositories(considerNestedRepositories = true, enableKeyspaceEvents = EnableKeyspaceEvents.ON_STARTUP),并在我的自定义购物车对象中添加了 eventListener() Bean。但是当它过期时,不会调用 lambda 函数。我也尝试将其用作组件,但 onApplicationEvent 方法没有被调用
    • 您是否尝试正确调试,您确定代码已正确实现。你能给我任何日志或示例实现,我可以检查它在我的本地实例上运行。
    • 是的,我也尝试过调试。这是一个基本工作的示例代码库bitbucket.org/marouissi/springdataredis/src/master
    • 嘿 Anuj 你有机会看到这个代码示例吗?你能做到吗
    猜你喜欢
    • 1970-01-01
    • 2018-09-08
    • 2020-09-26
    • 2016-04-25
    • 2021-01-01
    • 1970-01-01
    • 2019-11-01
    • 2019-04-22
    • 1970-01-01
    相关资源
    最近更新 更多