[Redis] Tutorial de aprendizaje de Redis (9) Publicación y suscripción de suscripción

1. Introducción a Pub/Sub

El patrón de publicación-suscripción (Pub/Sub) de Redis es un mecanismo de mensajería que permite el establecimiento de relaciones de comunicación poco acopladas entre remitentes y receptores. En este modelo, el remitente (editor) publica mensajes en un canal o esquema específico, y el receptor (suscriptor) puede suscribirse a uno o más canales para recibir mensajes publicados.

Los siguientes son los componentes principales del modelo de publicación-suscripción de Redis:

  • Editor: un editor es una entidad que genera y publica mensajes. Puede enviar mensajes a canales o modos específicos.
  • Suscriptor: Un suscriptor es una entidad que recibe y procesa mensajes. Puede suscribirse a uno o más canales o esquemas para recibir mensajes relevantes.
  • Canal: Un canal es un canal de comunicación entre editores y suscriptores. Los editores envían mensajes a los canales y los suscriptores reciben mensajes de los canales.

Como puede ver en la figura siguiente, la relación entre editor, suscriptor y canal es muy clara:

Insertar descripción de la imagen aquí
El editor publica un mensaje en el canal "Canal A": ¡Hola mundo! , todos los suscriptores del mensaje recibirán este mensaje

2. Utilice Pub/Sub para implementar la publicación y suscripción.

Hay dos modos para que Redis implemente la publicación/suscripción:

  1. Utilice canales para publicar y suscribirse
  2. Publicar y suscribirse usando patrones

Redis puede admitir múltiples bases de datos, cada una con su propio espacio de nombres y datos. El aislamiento, la partición y la organización de los datos se pueden lograr mediante el uso de múltiples bases de datos.

Pero vale la pena señalar:Este mecanismo de publicación-suscripción no tiene nada que ver con el espacio de partición de datos, por ejemplo, si un mensaje se publica en la base de datos 0, los suscriptores de otras zonas recibirán el mensaje.

Redis utiliza los siguientes comandos para operar el trabajo de Pub/Sub:

  • SUBSCRIBE:Suscríbete a uno o más canales
    • gramática:SUBSCRIBE channel [channel ...]
  • UNSUBSCRIBE : darse de baja de uno o más canales
    • gramática:UNSUBSCRIBE [channel [channel ...]]
  • PSUBSCRIBE:Suscribirse a uno o más esquemas
    • gramática:PSUBSCRIBE pattern [pattern ...]
  • PUNSUBSCRIBEDarse de baja de uno o más esquemas
    • gramática:PUNSUBSCRIBE [pattern [pattern ...]]
  • PUBSUB CHANNELS [pattern]: Listar canales activos
  • PUBSUB NUMSUB [channel-1 ... channel-N]: Muestra el número de suscriptores del canal.

2.1 Publicar y suscribirse a través de canales (Canal)

El proceso de publicación y suscripción a través de canales es el siguiente:

  1. El suscriptor se suscribe a un canal para monitorear el canal.
  2. El editor publica mensajes en el canal, el medio del centro de servicios.
  3. Todos los Suscriptores suscritos al Canal reciben el mensaje.

2.1.1 Los suscriptores se suscriben a canales

Después de suscribirse:

Insertar descripción de la imagen aquí
Utilice el cliente [suscriptor A] para suscribirse al canal [mychannel] para recibir mensajes. La información de respuesta se puede ver en lo anterior:

  • "suscribir": tipo de mensaje, la enumeración es suscribir, mensaje, cancelar suscripción
  • "mychannel": el nombre del canal
  • Contenido final del mensaje: los diferentes tipos de mensajes representan diferentes significados.

Luego de ingresar a la suscripción, el cliente puede recibir 3 tipos de mensajes enumerados:

  • suscribirse: el tipo de mensaje de suscripción exitosa, el segundo valor es el nombre del canal al que la suscripción fue exitosa y el tercer valor es la cantidad de canales actualmente suscritos por el cliente.
  • mensaje: el tipo de mensaje recibido por el cliente, el segundo valor indica el nombre del canal que generó el mensaje y el tercer valor es el contenido del mensaje.
  • cancelar suscripción: tipo de mensaje para cancelar la suscripción, el segundo valor es el nombre del canal correspondiente y el tercer valor es la cantidad de canales actualmente suscritos por el cliente. Cuando el valor es 0, significa que el cliente no tiene más suscripciones y sale del estado de suscripción.

2.1.2 El editor publica noticias

Hacer un anuncio:

Insertar descripción de la imagen aquí
Los mensajes publicados no se almacenarán de forma persistente, por lo que si un suscriptor suscribe el mensaje después de su publicación, el ciclo de vida del mensaje básicamente se completa.

2.1.3 Los suscriptores reciben mensajes

Para recibir el mensaje publicado anteriormente por el editor, nuestro cliente primero debe prestar atención al canal [mychannel] antes de recibir el mensaje "¡Hola, mundo!"

Insertar descripción de la imagen aquí

2.1.4 Darse de baja del canal

Si ya no deseas recibir mensajes de un canal, puedes cancelar la suscripción

2.2 Utilice la coincidencia de patrones para implementar la publicación y suscripción

Veamos otra solución para implementar publicar y suscribir, que es el método de coincidencia de patrones: además del cliente que se suscribe directamente, también verificará si hay un Canal que coincida con nuestro patrón, si es así, el mensaje también se publicará en el canal correspondiente. En el canal, los clientes que se suscriban a este canal también recibirán el mensaje

Como se muestra abajo:

Insertar descripción de la imagen aquí
Cuando el canal Message.Queue.Area1 recibe el mensaje, además del Actor A y el Actor B que se suscriben a su propio canal, pueden recibir el mensaje. Debido a que el canal coincide con el patrón correctamente, el mensaje también se envía a todos los suscritos al patrón Message.Queue.*.

Insertar descripción de la imagen aquí
Debido a que se utiliza el modo de coincidencia, el mensaje PUBLISH se publica fuera de Message.Queue.Area2. El canal también se comparará con el canal que coincida con el patrón. Si el canal coincide con un determinado patrón, el mensaje también se publicará para los clientes. quienes se suscriben a este patrón.

Por lo tanto, todas las líneas rojas de la imagen, incluidos el Actor C, el Actor D y el Actor E, han recibido el mensaje.

2.2.1 Los suscriptores se suscriben a canales

El cliente A se suscribe a Message.Queue.Area1:

Insertar descripción de la imagen aquí

El cliente B se suscribe a Message.Queue.Area2:

Insertar descripción de la imagen aquí
El cliente C se suscribe a Message.Queue.*:

Insertar descripción de la imagen aquí

2.2.2 El editor publica noticias

Insertar descripción de la imagen aquí

2.2.3 Los suscriptores reciben mensajes

Los suscriptores del canal correspondiente reciben el mensaje (Cliente A):

Insertar descripción de la imagen aquí
Los suscriptores que coinciden con el patrón reciben el mensaje (Cliente C):

Insertar descripción de la imagen aquí

Debido a que no existe una estrategia de filtrado, si se suscribe tanto al patrón coincidente (como Message.Queue.*) como al canal correspondiente (como Message.Queue.Area2), su cliente recibirá dos mensajes idénticos. Mensaje, un mensaje el tipo es mensaje, un tipo es pmessage

3. SpringBoot integra Redis para implementar el modelo de publicación-suscripción

3.1 Descripción general

Suscribirse a un mensaje significa recibir el mensaje, lo cual es más complicado. Existe administración tanto de conexiones de Redis como de grupos de subprocesos para consumir mensajes. Pero Spring ya ha hecho este "trabajo pesado".

Spring proporciona una solución completa, que incluye:

  1. Suscribirse/cancelar suscripción a estas acciones de usuario relacionadas
  2. Recibir todos los mensajes de Redis
  3. Distribuir estos mensajes a consumidores específicos según la relación de suscripción.
  4. El código de devolución de llamada que activa el mensaje de consumo se ejecuta en el grupo de subprocesos.

Dado que Spring tiene total autoridad, los usuarios solo necesitan proporcionar el tema que se consumirá y el código de devolución de llamada de consumo correspondiente.

Necesitamos comprender varias interfaces y clases proporcionadas por Spring antes de poder usarlas bien:

  1. TopicLa interfaz representa un objeto de suscripción: tiene dos clases de implementación, la ChannelTopicprimera PatternTopiccorresponde al canal de redis y la segunda corresponde al patrón de redis.
  2. MessageListenerInterfaz, interfaz de devolución de llamada, a través de la cual se ejecuta el código comercial
  3. MessageInterfaz, que representa mensajes recibidos de redis.
  4. RedisMessageListenerContainerclase, esta clase central es equivalente a un proxy, es decir, es responsable de recibir mensajes de redis y distribuirlos a MessageListener
  5. RedisConnectionFactory: RedisMessageListenerContainerNecesita esta clase RedisConnectionFactory: fábrica de conexiones de Redis, utilizada para obtener una conexión de Redis. Dado que esta conexión se utiliza para recibir mensajes, siempre está bloqueada.
  6. También puede especificar un Ejecutor, que es un grupo de subprocesos, para esta clase. Esto no es necesario. Si no lo especifica, generará un valor predeterminado.

Ensamblaje SpringBoot Mensajería Redis (Pub/Sub)

3.2 Usar publicar y suscribirse en Springboot

Primero hablemos de los pasos para usar la publicación y suscripción de Redis en Springboot:

  1. Configure la clase de escucha de mensajes (implemente MessageListenerla interfaz y anule el método onMessage()).
  2. Agregue un contenedor de escucha (configuración RedisMessageListenerContainer).
  3. Suscríbete al canal.
  4. Publica un mensaje en el canal.

3.2.1 Configurar la clase de escucha de mensajes

Agregue un oyente de mensajes de pedido:

@Component
public class OrderSubscriber implements MessageListener {
    
    

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void onMessage(Message message, byte[] pattern) {
    
    
        // 获取消息
        byte[] messageBody = message.getBody();
        // 使用值序列化器转换
        Object msg = redisTemplate.getValueSerializer().deserialize(messageBody);
        // 获取监听的频道
        byte[] channelByte = message.getChannel();
        // 使用字符串序列化器转换
        Object channel = redisTemplate.getStringSerializer().deserialize(channelByte);
        // 渠道名称转换
        String patternStr = new String(pattern);
        System.out.println(patternStr);
        System.out.println("---频道---: " + channel);
        System.out.println("---消息内容---: " + msg);
    }

}

3.2.2 El contenedor agrega oyente y se suscribe al canal

@Configuration
public class RedisConfig {
    
    

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    
    
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        // json 序列化配置
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // String 序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 所有的 key 采用 string 的序列化
        template.setKeySerializer(stringRedisSerializer);
        // 所有的 value 采用 jackson 的序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash 的 key 采用 string 的序列化
        template.setHashKeySerializer(stringRedisSerializer);
        // hash 的 value 采用 jackson 的序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory redisConnectionFactory, OrderSubscriber orderSubscriber) {
    
    
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        // 设置连接工厂
        container.setConnectionFactory(redisConnectionFactory);
        // 监听器订阅频道
        container.addMessageListener(orderSubscriber, new ChannelTopic("order"));
        container.addMessageListener(orderSubscriber, new ChannelTopic("sms"));
        // 序列化
        Jackson2JsonRedisSerializer seria = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        seria.setObjectMapper(objectMapper);
        container.setTopicSerializer(seria);
        return container;
    }

}
3.2.3.1 El contenedor agrega múltiples oyentes

Agregue un oyente de SMS:

@Component
public class SmsSubscriber implements MessageListener {
    
    

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void onMessage(Message message, byte[] pattern) {
    
    
        // 获取消息
        byte[] messageBody = message.getBody();
        // 使用值序列化器转换
    }
}

Cambio de configuracion:

@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory redisConnectionFactory, OrderSubscriber orderSubscriber, SmsSubscriber smsSubscriber) {
    
    
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    // 设置连接工厂
    container.setConnectionFactory(redisConnectionFactory);
    // 监听器订阅频道
    container.addMessageListener(orderSubscriber, Arrays.asList(new ChannelTopic("order"), new ChannelTopic("sms")));
    container.addMessageListener(smsSubscriber, new ChannelTopic("sms"));
    // 序列化
    Jackson2JsonRedisSerializer seria = new Jackson2JsonRedisSerializer(Object.class);
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    seria.setObjectMapper(objectMapper);
    container.setTopicSerializer(seria);
    return container;
}
3.2.3.2 Uso de PatternTopic
container.addMessageListener(smsSubscriber, new PatternTopic("redis.*"));

3.2.3 Publicar mensajes en canales

@RestController
@RequestMapping("/pub")
public class PubController {
    
    

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @GetMapping("/publish")
    public String publish() {
    
    
        redisTemplate.convertAndSend("order", "该订单已过期");
        redisTemplate.convertAndSend("sms", "该短信已发送");
        return "publish";
    }

}

3.3 Utilice MessageListenerAdapter para implementar publicación y suscripción

1. Definir una clase de recepción de mensajes.

@Component
public class OrderMessageReceiver {
    
    

    public void receiveMessage(String message, String channel){
    
    
        System.out.println("---频道---: " + channel);
        System.out.println("---消息内容---: " + message);
    }
}

2. Configurar un MessageListenerAdapter

@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory redisConnectionFactory, MessageListenerAdapter adapter, SmsSubscriber smsSubscriber) {
    
    
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    // 设置连接工厂
    container.setConnectionFactory(redisConnectionFactory);
    // 监听器订阅频道
    container.addMessageListener(adapter, Arrays.asList(new ChannelTopic("order"), new ChannelTopic("sms")));
    container.addMessageListener(smsSubscriber, new PatternTopic("redis.*"));
    // 序列化
    Jackson2JsonRedisSerializer seria = new Jackson2JsonRedisSerializer(Object.class);
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    seria.setObjectMapper(objectMapper);
    container.setTopicSerializer(seria);
    return container;
}

@Bean
public MessageListenerAdapter smsExpirationListener(OrderMessageReceiver messageListener) {
    
    
    MessageListenerAdapter receiveMessage = new MessageListenerAdapter(messageListener, "receiveMessage");
    // 序列化
    Jackson2JsonRedisSerializer seria = new Jackson2JsonRedisSerializer(Object.class);
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    seria.setObjectMapper(objectMapper);
    receiveMessage.setSerializer(seria);
    return receiveMessage;
}

Spring boot integra Redis para implementar publicación y suscripción (súper detallado)
Uso de publicación y suscripción de redis en spring boot

4. Resumen

Cuando se utiliza Pattern para publicar y suscribirse. Si se publica un mensaje, además del Cliente que se suscribe al Canal, todos los Clientes que se suscriban al patrón que coincida con el Canal también recibirán el mensaje.

Además, los mensajes de publicación y suscripción de Redis no persistirán, por lo que no hay mensajes históricos y el mecanismo ACK no es compatible.

Supongo que te gusta

Origin blog.csdn.net/sco5282/article/details/132800836
Recomendado
Clasificación