KeyExpirationEventMessageListener Key expires to listen to events, business problems that occur in the service cluster

The project has a business scenario.When a certain time node is reached, a message notification is sent to the user's public account.

Because the time point is not fixed, no timed task is
used . Use the redis key invalidation listener to do it. The idea is to save the key and
calculate the current and the time interval when the notification needs to be sent as the key expiration time. Time to send a message in real time.

The test push is normal in stand-alone mode, but when online, the user received two pushes, because the service opened the cluster, and each service received a notification when the key became invalid. At this time, the message was pushed, so it happened The issue of pushing multiple messages.

At this time, you can consider using redis' setNx command to achieve lock competition. At the same time, only one service can preempt the same key, that is, send a message, and other services that have not obtained the lock, then give up directly to perform message push.

The principle of the setNx command is that if the key does not exist, it returns 1 if the save is successful, otherwise it returns 0.
The redis command is executed by a single thread, so only one person can get a successful result in the same time period.

The implementation code is as follows

@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {

    @Autowired
    private RedisRepository redisRepository;

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

    @Override
    public void onMessage(Message message, byte[] pattern) {
        String expiredKey = message.toString();
        // 获取锁 重点,此处就是多个服务或者线程对同一个key进行抢占的setNx命令调用
        if(redisRepository.lockBySecondsTime(key, 30)){
            // 消息通知伪代码
			push();
        }
        super.onMessage(message, pattern);
    }

}

The lockBySecondsTime lock method implementation logic is as follows


    public boolean lockBySecondsTime(String key, long expirationTime){
        //  此方法只适用于在 expirationTime 时间段内进行锁竞争的场景。如果超过 expirationTime 时间段,锁自动失效,之前获取到锁的线程还在运行,就失去了分布式锁的意义,慎重根据自己的场景来使用。
        Long timeStamp = new Date().getTime() + (expirationTime * 1000);
        // 通过setNx获取锁
        return ifAbsent(key, String.valueOf(timeStamp), expirationTime, TimeUnit.SECONDS);
    }

	public boolean ifAbsent(String key, String value, long expirationTime , TimeUnit timeUnit) {
        Boolean res = (Boolean) redisTemplate.execute(new RedisCallback() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.stringCommands().set(key.getBytes(), value.getBytes(),
                        Expiration.from(expirationTime, timeUnit), RedisStringCommands.SetOption.ifAbsent());
            }
        });
        return res == null ? false : res;
    }


The above code is dependent on the SpringDataRedis module. Pay attention to the ifAbsent method. The redisTemplate.execute method is used here. Why should I do this?

If you are version 2.1.0 or higher, you can directly complete the ifAbsent operation in the above code through redisTemplate.opsForValue (). SetIfAbsent (key, value, time).

However, the version below 2.1.0 only has the redisTemplate.opsForValue (). SetIfAbsent (key, value) method, which lacks a set key expiration time. If this method is used, the key will always be stored in redis, causing waste of memory space resources. So at this time, the author uses redisTemplate.execute to expand it to ensure that this key will disappear by itself after being used as a lock competition resource.

The redis lock method is used, so that the same key operation can only be done by one service at a time, so my message notification is normal at this time, and there will be no repeated message push ~

Recommend a distributed lock framework based on redis redisson If you are interested, you can learn about it ~

Published 38 original articles · praised 17 · visit 9023

Guess you like

Origin blog.csdn.net/cainiao1412/article/details/98881997