Redis publish / subscribe

Posted by jeremyphphaven on Tue, 04 Jan 2022 19:15:55 +0100

Redis implements the publish / subscribe function, and developers can quickly carry out scenario applications through this lightweight function.
SUBSCRIBE,UNSUBSCRIBE and PUBLISH Realized Publish / subscribe messaging paradigm , this decoupling between publishers and subscribers can achieve greater scalability and more dynamic network topology.

Note: Redis itself does not store message body information. If the actual production environment is consuming and the network fluctuation causes one of the consumers to hang up for a period of time, when it is reconnected, the messages generated during this period will not be consumed by the consumer.

Pub/Sub

Format of push message

The message is an array of three elements.

The first element is the message type:

  • Subscribe: indicates that we have successfully subscribed to the channel given as the second element in the reply. The third parameter represents the number of channels we currently subscribe to.
  • unsubscribe: indicates that we have successfully unsubscribed the channel given as the second element in the reply. The third parameter represents the number of channels we currently subscribe to. When the last parameter is zero, we will no longer subscribe to any channel. The client can issue Redis commands of any type because we are outside the Pub/Sub state.
  • Message: it is a message received due to the PUBLISH command issued by another client. The second element is the name of the originating channel, and the third parameter is the actual message payload.
# Subscribe to msg and chat_room two channels

# Lines 1 - 6 are the feedback information after executing subscribe
# Lines 7 - 9 are the first message received
# Lines 10 - 12 are the second

redis> subscribe msg chat_room
Reading messages... (press Ctrl-C to quit)
1) "subscribe"       # Type of return value: display subscription success
2) "msg"             # Subscribed channel name
3) (integer) 1       # Number of channels currently subscribed

1) "subscribe"
2) "chat_room"
3) (integer) 2

1) "message"         # Type of return value: Information
2) "msg"             # Source (from which channel)
3) "hello moto"      # information content

1) "message"
2) "chat_room"
3) "testing...haha"

Database and scope

Pub/Sub has nothing to do with the key space. It is designed not to be disturbed by key space at any level, including database number.

Publish on db 10 and will be heard by subscribers on db 1.

If you need a range of a range, please add the environment name (test, pre release, production...) before the channel.

Messages matching patterns and channel subscriptions

If a client subscribes to multiple patterns that match a published message, or subscribes to patterns and channels that match the message, the client may receive a message multiple times. As shown in the following example:

SUBSCRIBE foo
PSUBSCRIBE f*

In the above example, if a message is sent to channel foo, the client will receive two messages: one is type message and the other is type pmessage.

storage structure

redisServer. pubsub_ The patterns attribute is a linked list that holds all information related to patterns. Each node in the linked list contains a redis H / pubsubpattern structure

struct redisServer {
    // ...
    list *pubsub_patterns;
    // ...
};

typedef struct pubsubPattern {
    redisClient *client;
    robj *pattern;
} pubsubPattern;

The client attribute holds the client of the subscription mode, while the pattern attribute holds the subscribed mode.

Whenever the PSUBSCRIBE command is called to subscribe to a pattern, the program creates a pubsubPattern structure containing client information and the subscribed pattern, and adds the structure to redisserver pubsub_ Patterns in the linked list.

As an example, the following figure shows a PubSub with two patterns_ Patterns linked list, where both client123 and client256 are subscribing to tweets shop.* pattern:

If client client 10086 executes psubscribe broadcast list.*, So PubSub_ The patterns linked list will be updated as follows:

By traversing the entire pubsub_patterns linked list. The program can check all the patterns being subscribed and the clients subscribing to these patterns.

Code example

bean registration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({  RedisAutoConfiguration.class })
@AutoConfigureBefore(RedissonAutoConfiguration.class)
public class MyRedisAutoConfiguration {

    @Bean
    public NameUpdateSubscriber nameUpdateSubscriber(){
        return new NameUpdateSubscriber();
    }

    @Bean
    public RedisMessageListenerContainer container(@Qualifier("redisConnectionFactory") RedisConnectionFactory connectionFactory, NameUpdateSubscriber nameUpdateSubscriber) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        //Configure subscription items to subscribe to
        container.addMessageListener(nameUpdateSubscriber, new PatternTopic(RedisConstant.NAME_UPDATE_CHANNEL));
        return container;
    }
}

Push message

public void delNameCache(Long id) {
    Boolean delete = stringRedisTemplate.delete(getCacheKey(id));
    if (Objects.nonNull(delete) && delete) {
        stringRedisTemplate.convertAndSend(RedisConstant.NAME_UPDATE_CHANNEL, String.valueOf(id));
    }
}

Consumption monitoring

public class GuildNameUpdateSubscriber extends MessageListenerAdapter {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private NameRepository nameRepository;

    public NameUpdateSubscriber() {
    }

    @Override
    public void onMessage(Message message, byte[] pattern) {
        byte[] body = message.getBody();
        byte[] channel = message.getChannel();
        String msg = redisTemplate.getStringSerializer().deserialize(body);
        String topic = redisTemplate.getStringSerializer().deserialize(channel);
        log.info("Monitor channel by {} Message:{}", topic, msg);
        if (Objects.equals(topic, RedisConstant.NAME_UPDATE_CHANNEL) && StringUtils.isNotBlank(msg)) {
            nameRepository.refreshCache(Long.parseLong(msg));
        }
    }
}

reference material:

  1. Redis publish / subscribe
  2. Implementation of Redis publish / subscribe
  3. How to use Redis's publish subscribe function in Spring Boot
  4. Redis Command Reference

Topics: Database Redis Cache