Do not rely too much on the expired monitoring of redis!!

Redis expired monitoring scenario

In business, there is a need to wait for a certain time to execute a certain behavior, such as closing the order after 30 minutes There are many online demos that use redis expired monitoring, but in fact, this is a big hole because redis can't ensure that the key is deleted at the specified time, resulting in the delay of notification No more, run a test

Test situation

Let's start with the environment. Redis runs in the docker container, allocates a CPU and 512MB of memory, and executes redis benchmark - t set - R 100000 - N 1000000 in the docker. The results are as follows:

\====== SET ======
  1000000 requests completed in 171.03 seconds
  50 parallel clients
  3 bytes payload
  keep alive: 1
  host configuration "save": 3600 1 300 100 60 10000
  host configuration "appendonly": no
  multi-thread: no

In fact, there are some imprecise benchmark threads that should not run inside the docker container When running points, the main threads of benchmark and redis each hold 50% CPU

The test code is as follows:

@Service
@Slf4j
public class RedisJob {
    @Autowired
    private StringRedistemplate stringRedistemplate;

    public DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    public LocalDateTime end = LocalDateTime.of(LocalDate.of(2020,5,12),LocalTime.of(8,0));

    @Scheduled(cron = "0 56 \* \* \* ?")
    public void initKeys() {
        LocalDateTime Now = LocalDateTime.Now();
        ValueOperations<String,String> operations = stringRedistemplate.opsForValue();
        log.info("开始设置key");
        LocalDateTime begin = Now.withMinute(0).withSecond(0).withNano(0);
        for (int i = 1; i < 17; i++) {
            setExpireKey(begin.plusHours(i),8,operations);
        }
        log.info("设置完毕: " + Duration.between(Now,LocalDateTime.Now()));
    }

    private void setExpireKey(LocalDateTime expireTime,int step,ValueOperations<String,String> operations) {
        LocalDateTime localDateTime = LocalDateTime.Now().withNano(0);
        String NowTime = dateTimeFormatter.format(localDateTime);
        while (expireTime.getMinute() < 55) {
            operations.set(NowTime + "@" + dateTimeFormatter.format(expireTime),"A",Duration.between(expireTime,LocalDateTime.Now()).abs());
            expireTime = expireTime.plusSeconds(step);
        }
    }
}

At 56 minutes per hour, a batch of keys that expire in the next 16 hours will be added. The expiration time interval is 8 seconds, and the expiration time is before 55 minutes

@Slf4j
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {

    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

    public DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    @Autowired
    private StringRedistemplate stringRedistemplate;


    @Override
    public void onMessage(Message message,byte\[\] pattern) {
        String keyName = new String(message.getBody());
        LocalDateTime parse = LocalDateTime.parse(keyName.split("@")\[1\],dateTimeFormatter);
        long seconds = Duration.between(parse,LocalDateTime.Now()).getSeconds();
        stringRedistemplate.execute((RedisCallback<Object>) connection -> {
            Long size = connection.dbSize();
            log.info("过期key:" + keyName + ",当前size:" + size + ",滞后时间" + seconds);
            return null;
        });
    }
}

Here is the current dbsize and lag time of printing after expiration is detected

@Bean
public RedisMessageListenerContainer configRedisMessageListenerContainer(RedisConnectionFactory connectionFactory) {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(100);
    executor.setMaxPoolSize(100);
    executor.setQueueCapacity(100);
    executor.setKeepAliveSeconds(3600);
    executor.setThreadNamePrefix("redis");
    // rejection-policy:当pool已经达到max size的时候,如何处理新任务
    // CALLER\_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.initialize();
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    // 设置Redis的连接工厂
    container.setConnectionFactory(connectionFactory);
    // 设置监听使用的线程池
    container.setTaskExecutor(executor);
    // 设置监听的Topic
    return container;
}

Set the expiration listening and thread pool information of redis,

The final test result is that when the number of keys is less than 10000, basically the expiration notification can be completed within 10s, but if the number reaches 30000, some keys will be delayed by 120s By the way, post my latest diary

2020-05-13 22:16:48.383  : 过期key:2020-05-13 11:56:02@2020-05-13 22:14:08,当前size:57405,滞后时间160
2020-05-13 22:16:49.389  : 过期key:2020-05-13 11:56:02@2020-05-13 22:14:32,当前size:57404,滞后时间137
2020-05-13 22:16:49.591  : 过期key:2020-05-13 10:56:02@2020-05-13 22:13:20,当前size:57403,滞后时间209
2020-05-13 22:16:50.093  : 过期key:2020-05-13 20:56:00@2020-05-13 22:12:32,当前size:57402,滞后时间258
2020-05-13 22:16:50.596  : 过期key:2020-05-13 07:56:03@2020-05-13 22:13:28,当前size:57401,滞后时间202
2020-05-13 22:16:50.697  : 过期key:2020-05-13 20:56:00@2020-05-13 22:14:32,当前size:57400,滞后时间138
2020-05-13 22:16:50.999  : 过期key:2020-05-13 19:56:00@2020-05-13 22:13:44,当前size:57399,滞后时间186
2020-05-13 22:16:51.199  : 过期key:2020-05-13 20:56:00@2020-05-13 22:14:40,当前size:57398,滞后时间131
2020-05-13 22:16:52.205  : 过期key:2020-05-13 15:56:01@2020-05-13 22:16:24,当前size:57397,滞后时间28
2020-05-13 22:16:52.808  : 过期key:2020-05-13 06:56:03@2020-05-13 22:15:04,当前size:57396,滞后时间108
2020-05-13 22:16:53.009  : 过期key:2020-05-13 06:56:03@2020-05-13 22:16:40,当前size:57395,滞后时间13
2020-05-13 22:16:53.110  : 过期key:2020-05-13 20:56:00@2020-05-13 22:14:56,当前size:57394,滞后时间117
2020-05-13 22:16:53.211  : 过期key:2020-05-13 06:56:03@2020-05-13 22:13:44,当前size:57393,滞后时间189
2020-05-13 22:16:53.613  : 过期key:2020-05-13 15:56:01@2020-05-13 22:12:24,当前size:57392,滞后时间269
2020-05-13 22:16:54.317  : 过期key:2020-05-13 15:56:01@2020-05-13 22:16:00,当前size:57391,滞后时间54
2020-05-13 22:16:54.517  : 过期key:2020-05-13 18:56:00@2020-05-13 22:15:44,当前size:57390,滞后时间70
2020-05-13 22:16:54.618  : 过期key:2020-05-13 21:56:00@2020-05-13 22:14:24,当前size:57389,滞后时间150
2020-05-13 22:16:54.819  : 过期key:2020-05-13 17:56:00@2020-05-13 22:14:40,当前size:57388,滞后时间134
2020-05-13 22:16:55.322  : 过期key:2020-05-13 10:56:02@2020-05-13 22:13:52,当前size:57387,滞后时间183
2020-05-13 22:16:55.423  : 过期key:2020-05-13 07:56:03@2020-05-13 22:14:16,当前size:57386,滞后时间159

It can be seen that when the number reaches 50000, most of them have lagged behind by two minutes, which is completely unbearable for the business side

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>