Redis of springboot sends messages regularly

Time:2021-11-28

Redis of springboot sends messages regularly

1、 Demand

Send timed announcements in real time. The countdown function monitors redis cache expiration (key failure) events. Similar uses can be used for regular closing of orders, loading and unloading of goods or activities.

2、 Modify the redis.conf file, open the note of notify keyspace events ex, and enable the expiration notification function

############################# EVENT NOTIFICATION ##############################

# Redis can notify Pub/Sub clients about events happening in the key space.
# This feature is documented at http://redis.io/topics/notifications
#
# For instance if keyspace events notification is enabled, and a client
# performs a DEL operation on key "foo" stored in the Database 0, two
# messages will be published via Pub/Sub:
#
# PUBLISH [email protected]__:foo del
# PUBLISH [email protected]__:del foo
#
# It is possible to select the events that Redis will notify among a set
# of classes. Every class is identified by a single character:
#
#  K     Keyspace events, published with [email protected]<db>__ prefix.
#  E     Keyevent events, published with [email protected]<db>__ prefix.
#  g     Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...
#  $     String commands
#  l     List commands
#  s     Set commands
#  h     Hash commands
#  z     Sorted set commands
#  x     Expired events (events generated every time a key expires)
#  e     Evicted events (events generated when a key is evicted for maxmemory)
#  A     Alias for g$lshzxe, so that the "AKE" string means all the events.
#
#  The "notify-keyspace-events" takes as argument a string that is composed
#  of zero or multiple characters. The empty string means that notifications
#  are disabled.
#
#  Example: to enable list and generic events, from the point of view of the
#           event name, use:
#
#  notify-keyspace-events Elg
#
#  Example 2: to get the stream of the expired keys subscribing to channel
#             name [email protected]__:expired use:
#
#  notify-keyspace-events Ex
#
#  By default all notifications are disabled because most users don't need
#  this feature and the feature has some overhead. Note that if you don't
#  specify at least one of K or E, no events will be delivered.
notify-keyspace-events ""

3、 Restart redis to test whether the listening event is enabled

[email protected]__: Expired is actuallyIt refers to all libraries. You can specify that the library subscript listens to one of the 16 default databases, such as[email protected] Specify number oneLibrary. openredisclientA, the psubscribe directive subscribes to events.

127.0.0.1:6379> PSUBSCRIBE [email protected]*__:expired
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "[email protected]*__:expired"
3) (integer) 1

Open another oneredisclientB, send the expired data to the specified event for 2 seconds.

127.0.0.1:6379> setex test 2 2
OK

Redisclienta will listen to the expired key of redisclientb

127.0.0.1:6379> PSUBSCRIBE [email protected]*__:expired
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "[email protected]*__:expired"
3) (integer) 1
1) "pmessage"
2) "[email protected]*__:expired"
3) "[email protected]__:expired"
4) "test"

4、 Listener configuration and Implementation

Keyexpirationevent1messagelistener can be implemented by referring to the source code of org.springframework.data.redis.listener.keyexpirationeventmessagelistener (the default subscription is[email protected]*: expired), and our goal is to monitor library 1. Because library 0 is used for current limiting and oauth2, there are many short-term keys in it, which will listen to many irrelevant business key caches. In addition, you cannot add @ component to keyexpirationevent1messagelistener because there is a bean circular dependency problem, which can be solved through springcontextholder.

@Slf4j
public class KeyExpirationEvent1MessageListener extends KeyExpirationEventMessageListener {

    private static final Topic KEYEVENT1_EXPIRED_TOPIC = new PatternTopic("[email protected]__:expired");

    /**
     * @param listenerContainer must not be {@literal null}.
     */
    public KeyExpirationEvent1MessageListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

    @Override
    public void doRegister(RedisMessageListenerContainer listenerContainer) {
        listenerContainer.addMessageListener(this, KEYEVENT1_EXPIRED_TOPIC);
    }

    @Override
    public void onMessage(Message message, byte[] pattern) {
        //Get expired key
        String expireKey = message.toString();
        //Set listening channel
        if (expireKey.startsWith(RedisConstant.NOTIFY_RECEIVE)) {
            Log.info ("message ID of expired key value pair:" + expirekey ");
            Log.info ("message listening channel topic:" + new string (message. Getchannel())));
//Get the message sending ID through
            String sendId = expireKey.substring(RedisConstant.NOTIFY_RECEIVE.length());
            SysNotifySendService sysNotifySendService =  SpringContextHolder.getBean(SysNotifySendService.class);
//Common service provides websocket sending remote interface remotecommonservice
            RemoteCommonService remoteCommonService =  SpringContextHolder.getBean(RemoteCommonService.class);
            SysNotifyReceiveService receiveService =  SpringContextHolder.getBean(SysNotifyReceiveService.class);
            SysNotifySend sysNotifySend = sysNotifySendService.getOne(Wrappers.<SysNotifySend>lambdaQuery().eq(SysNotifySend::getSendId, sendId));
            com.gdjs.gold.admin.api.dto.Message websocketMsg = new com.gdjs.gold.admin.api.dto.Message();
            websocketMsg.setSendId(sysNotifySend.getSendId());
            websocketMsg.setFrom(sysNotifySend.getSendUserId());
            websocketMsg.setDestination(ThemeEnum.BUSINESS.getTheme());
            websocketMsg.setMessage(sysNotifySend.getContent());
            remoteCommonService.sendMessage(websocketMsg);     
        }
    }

}
@Configuration
public class RedisListenerConfig {

    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, ApplicationContext context) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
//Pass the new listener and register the listener with redismessagelistenercontainer
        KeyExpirationEvent1MessageListener listener = new KeyExpirationEvent1MessageListener(container);
        listener.doRegister(container);
        listener.setApplicationEventPublisher(context);
        return container;
    }

}

5、 How to customize and switch the specified redis database subscript when sending

Springboot before 1. X

JedisConnectionFactory jedisConnectionFactory = (JedisConnectionFactory) stringRedisTemplate.getConnectionFactory();
Jedisconnectionfactory.setdatabase (switch to the specified dB);
stringRedisTemplate.setConnectionFactory(jedisConnectionFactory);

For the version after springboot 2. X, the redisconnectionfactory dynamic switching library must be letticconnectionfactory, which must be specified when configuring redistemplate. See the source code below.

public class RedisUtil {
    /**
     *Switch redis database
     *
     *@ param redistemplate redis object encapsulated by springboot
     *@ param index database subscript
     */
    public static void select(RedisTemplate redisTemplate, int index) {
        LettuceConnectionFactory lettuceConnectionFactory = (LettuceConnectionFactory) redisTemplate.getConnectionFactory();
        if (lettuceConnectionFactory != null) {
            lettuceConnectionFactory.setDatabase(index);
            redisTemplate.setConnectionFactory(lettuceConnectionFactory);
            lettuceConnectionFactory.resetConnection();
        }
    }

}
/**
 * @author caochikai
 * @date 2019/7/12
 *Redis configuration class
 */
@EnableCaching
@Configuration
@AllArgsConstructor
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisTemplateConfig {
//@Allargsconstructor is the lettucconnectionfactory injected by the constructor
    private final LettuceConnectionFactory lcfactory;

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        //Close the shared link, and the focus of dynamic switching is here
        **lcfactory.setShareNativeConnection(false);**
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setConnectionFactory(lcfactory);
        return redisTemplate;
    }

    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForHash();
    }

    @Bean
    public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
        return redisTemplate.opsForValue();
    }

    @Bean
    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForList();
    }

    @Bean
    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForSet();
    }

    @Bean
    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForZSet();
    }
}

6、 Tips for setting cache

The key naming rule prefix redisconstant.notify needs to be specified_ Receive constant (usually the table name), followed by the primary key of the table. Tips for calculating time interval: Bloggers use localdatetime, duration. Between (localdatetime. Now(), specify the sending time). Getseconds(), and the expiration time unit uses timeunit enumeration. After switching to the designated library 1, remember to switch back to library 0 to reduce the impact on other businesses

/**
     *Redis cache expiration monitoring
     *
     *@ param sysnotifysend sends messages regularly
     *@ param message message content
     */
    private void redisSetMsgKey(SysNotifySend sysNotifySend, Message message) {
        LocalDateTime sendTime = sysNotifySend.getSendTime();
        String jsonString = JSONUtil.parseObj(message).toJSONString(0);
        RedisUtil.select(redisTemplate, 1);
        redisTemplate.opsForValue().set(RedisConstant.NOTIFY_RECEIVE + sysNotifySend.getSendId(), jsonString, Duration.between(LocalDateTime.now(), sendTime).getSeconds(), TimeUnit.SECONDS);
        RedisUtil.select(redisTemplate, 0);
    }

7、 Reference articles are as follows:

  1. Spring boot 2.0 and above integrate redis to dynamically switch databases according to subscripts
  2. Redis cache expiration policy, listening to redis cache

Recommended Today

On the mutation mechanism of Clickhouse (with source code analysis)

Recently studied a bit of CH code.I found an interesting word, mutation.The word Google has the meaning of mutation, but more relevant articles translate this as “revision”. The previous article analyzed background_ pool_ Size parameter.This parameter is related to the background asynchronous worker pool merge.The asynchronous merge and mutation work in Clickhouse kernel is completed […]