There are three ways for SpringBoot to integrate redis keys (expired, new, modified). Just read this article.

The article mainly describes three ways for Springboot to integrate key changes, and also lists some integration pitfalls and concepts.

principle

The principle of SpringBoot's integration of Redis key changes is that it remains the same. To put it simply:
spring-boot-starter-data-redis + notify-keyspace-events

About notify-keyspace-events

  • notify-keyspace-events AKExIs a command in Redis used to configure the type of notifications sent by the server. The parameters here AKExrepresent key space notification and key event notification.

  • Specifically, Ait indicates receiving all types of notifications, Kindicating receiving key space notifications, and Exindicating receiving key event notifications. Keyspace notifications are about changes to the entire database, while key event notifications are about changes to specific keys.

  • For example, if you execute SET mykey "Hello"the command in Redis, the client using notify-keyspace-events AKExthe configuration will receive a mykeykey event notification for the key.

  • It should be noted that to use notify-keyspace-eventsthe command, the relevant notify mechanism must be enabled in the Redis server, otherwise the client cannot receive any notifications. At the same time, this command can only send notifications when the value of Redis string 类型键is modified. Modifications of other types of keys (such as hash, list, set, sorted set, etc.) will not trigger notifications.

Message topic about redis (Topic)

Redis message topic (Topic) is a keyword used to publish and subscribe to messages. According to the design of Redis, it only supports messages in the publish/subscribe model and does not support messages in the request/response model.

In Redis, you can use PSUBSCRIBEthe command to subscribe to one or more topics and listen to related messages. Here are some common Redis message topics:

  • __keyevent@*__:expired: Expiration event topic, publishes all library expiration messages, *indicating all.
  • __keyevent@0__:expired0: Expiration event topic, representative of publishing expiration messages in db0 database db0.
  • __keyevent@1__:delDEL: Event triggered when a key is deleted using the command.
  • __keyevent@4__:renameRENAME: Event triggered when a key is renamed using the command.
  • __keyevent@5__:setSET: Event triggered when the value of a key is set using the command.

These topics were introduced in Redis 4.0 and are used to represent key-specific events. By subscribing to the corresponding topic, you can listen to related events and process them accordingly.
In addition to the topics listed above, Redis also supports custom topics. Users can define and subscribe to related topics according to their own needs.

Ok, after understanding the above concepts, let’s go directly to the code to see how to rewrite the monitoring method.

Rewrite the listener

  1. Enable redis key change event
    redis configuration file configuration notify-keyspace-events AKEx, the default is关闭的
  2. Introduce dependencies (other normal dependencies are omitted, remember to add them)
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
  1. Write a RedisListenerConfigconfiguration file (the file name is whatever you want, it doesn’t have to be the same as the blogger)


@Configuration
public class RedisListenerConfig {
    
    
	//配置redis监听容器
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
    
    
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
    //配置redis的序列化策略
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
    
    
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);//所有属性均可见
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);//为null不参加序列化
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);//在Redis中存储对象类信息
        jackson2JsonRedisSerializer.setObjectMapper(mapper);
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.setConnectionFactory(factory);
        return template;
    }
}
  1. Write a RedisKeyExpirationListenerlistener
@Component
@Slf4j
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
    
    

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

    /**
     * 针对redis数据失效事件,进行数据处理
     * @param message message must not be {@literal null}. 过期的key
     * @param pattern pattern matching the channel (if specified) - can be {@literal null}. 队列名称
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
    
    
        // 拿到key
        String expiredKey = message.toString();
        log.info("监听Redis key过期,key:{},channel:{}", expiredKey, new String(pattern));
    }
}

At this point, key expiration monitoring is completed. How to update and delete keys in this way?

KeyExpirationEventMessageListener This class is an expiration listening class encapsulated by Springboot. Let’s take a look at the source code.

Insert image description here

But Springboot does not encapsulate updates and deletions with you. So we must learn to draw inferences from one example.

  • The first step is to copy KeyExpirationEventMessageListener, assuming the name isKeyUpdateEventMessageListener
  • Modify __keyevent@0__:expiredto __keyevent@0__:setthis is an updated topic
  • Write RedisKeyUpdateListener extent KeyUpdateEventMessageListener, rewrite onMessagethe method

ok, now that I have finished writing, here are some tips. File type conversion windows tool developed in Python, supports mutual conversion of png, jpeg, ico and other file types.

Let’s continue to look at the container registration method

Container registration

The above configuration will not be rewritten.

  1. Note that we write RedisKeyUpdatedListenerimplementsMessageListener
@Component
@Slf4j
public class RedisKeyUpdatedListener implements MessageListener {
    
    

    /**
     * key 更新监听
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
    
    
        // 拿到key
        String expiredKey = message.toString();
        log.info("监听Redis keyg更新,key:{},channel:{}", expiredKey, new String(pattern));
    }
}
  1. Then modify the container configuration
@Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
    
    
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        //监听指定db0 的key set事件 *表示监听所有的db
        container.addMessageListener(redisKeyUpdatedListener, new PatternTopic("__keyevent@*__:set"));
        return container;
    }

It should be noted here that redisKeyUpdatedListenerit is injected through injection, not new, because if it is through new. When the listener has business logic, multiple business service components will be introduced. Through new, it can only be passed in through construction, and the service component is injected from the configuration class.

If there are more than one, as follows:

container.addMessageListener(redisKeyUpdatedListener, new PatternTopic("__keyevent@*__:set"));
container.addMessageListener(redisKeyExpiredListener, new PatternTopic("__keyevent@*__:expired"));
container.addMessageListener(redisKeyDelListener, new PatternTopic("__keyevent@*__:del"));

Custom analysis

This is more convenient. Let’s look at the code directly, but the coupling degree is a bit high and does not comply with the design pattern specifications.

@Component
public class CustomRedisMessageListener implements MessageListener {
    
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Override
    public void onMessage(Message message, byte[] pattern) {
    
    
        String ops = new String(message.getBody());     //操作
        String channel = new String(message.getChannel());
        String key = channel.split(":")[1];
		//对redis的新增或删除事件进行监听
        if ("set".equals(ops)) {
    
    
            String value = redisTemplate.opsForValue().get(key);
            handleSet(key, value);
        } else if ("del".equals(ops)) {
    
    
            handleDel(key);
        }
    }

    /**
     * 监听新增 处理逻辑
     */
    private void handleSet(String key, String value) {
    
    
			//将数据同步刷新到内存中
            gatewayCache.refreshApiWhitelistsCache(id, JsonUtil.toObject(value, Set.class));
    }

    /**
     * 监听删除 处理逻辑
     * @param key 被删除的key
     */
    private void handleDel(String key) {
    
     
            gatewayCache.deleteApiWhitelists(id);
    }
}

Common integration issues

Why did I introduce the package and the code is extremely correct? Why can’t I monitor the key after it has expired?

  • Make sure the project can start
  • Make sure there are no conflicting dependencies
  • Make sure redis configuration is enablednotify-keyspace-events AKEx

Why did I configure notify-keyspace-events. Is it because the key update event is not detected?

  • Make sure that configured is notify-keyspace-events AKExand not notify-keyspace-events Ex. ex can only listen to expiration events, but not deletion events.

Acknowledgments

  • Thank you very much for reading this article from beginning to end. I hope the content is inspiring and helpful to you. If you have other questions or need further information, please feel free to follow my updates and leave a message
  • Finally, I hope everyone will give the author some attention and support. It is not easy to create.
  • If you think it is worth collecting, you can also collect it.
  • Finally, here are some tips for you. Elegantly encapsulates interfaces for third-party calls

Guess you like

Origin blog.csdn.net/qq_40673786/article/details/132551197