Redis(spring data redis) 发布订阅 pub/sub

Spring Data为Redis提供了专用的消息集成,功能类似,并命名为Spring Framework中的JMS集成。

Redis消息大致可分为两个功能区域:

  • 发布或生产消息

  • 订阅或消费消息

通常被称为发布/订阅模式。RedisTemplate类是用来生产消息的。对于类似于Java EE的消息驱动bean形式的异步接收,Spring Data提供了一个专用的消息监听器容器,用于创建消息驱动的POJO(MDP),以及用于同步接收的RedisConnection约定

org.springframework.data.redis.connection和org.springframework.data.redis.listener两个包提供了redis消息的核心功能。

1 发布(发送消息)

要发布消息,您可以像使用其他操作一样使用低级别RedisConnection或高级别RedisTemplate。两个实体都提供该publish方法,该方法接受消息和目标通道作为参数。虽然RedisConnection需要原始数据(字节数组),但RedisTemplate允许任意对象作为消息传入,如以下示例所示:

// send message through connection RedisConnection con = ...
byte[] msg = ...
byte[] channel = ...
con.publish(msg, channel); // send message through RedisTemplate
RedisTemplate template = ...
template.convertAndSend("hello!", "world");

2 订阅(接收消息)

在接收方,可以通过直接命名或使用模式匹配来订阅一个或多个通道。后一种方法非常有用,因为它不仅可以使用一个命令创建多个订阅,还可以监听尚未在订阅时创建的通道(只要它们与模式匹配)。

在使用低级别RedisConnection时,它提供了映射Redis命令的方法subscribepSubscribe方法,分别按通道或模式进行订阅。请注意,可以使用多个通道或模式作为参数。要更改连接的订阅或查询是否正在侦听,请RedisConnection提供getSubscriptionisSubscribed方法。

Spring Data Redis中的订阅命令是阻塞的。也就是说,在连接上调用subscribe会导致当前线程在开始等待消息时阻塞。只有在在同一连接上的另一个线程调用unsubscribepUnsubscribe取消订阅时才会释放该线程。有关此问题的解决方案,请参阅“ 消息侦听器容器 ”(本文档后面部分)。因此,一旦在一个连接上开始订阅,该连接便开始等待接受消息,并且在该条连接上只能执行新建订阅,修改已有订阅,或取消订阅等命令。

如果想要订阅消息,需要实现MessageListener的回调。当接收到一个消息时,都会执行回调和onMessage中的代码。该接口不仅可以访问实际消息,还可以访问通过它接收的通道以及订阅用于匹配通道的模式(如果有)。这使被调用的一方不仅可以通过内容区分各种消息,还可以检查其他详细信息。

2.1 Message Listener Containers 消息侦听器容器

由于阻塞性质,低级(连接)订阅不具吸引力,因为它需要为每个侦听器提供连接和线程管理。为了缓解这个问题,Spring Data提供了RedisMessageListenerContainer来处理所有繁重的工作。如果您熟悉EJB和JMS,那么您应该找到熟悉的概念,因为它被设计的尽可能接近Spring Framework及其消息驱动的POJO(MDP)中的支持。

RedisMessageListenerContainer作为一个消息监听容器,被用来接受redis通道中的消息并驱动被注入的MessageListener实例。该容器负责消息接受的所有线程并分发给正在进行中的监听器,消息监听器容器是MDP和消息传递提供者之间的中介,并负责注册以接收消息,资源获取和释放,异常转换等。这使您作为应用程序开发人员可以编写与接收消息(并对其做出反应)相关联的(可能是复杂的)业务逻辑,并将样板Redis基础结构关注委托给框架。

此外,为了最小化应用程序占用空间,RedisMessageListenerContainer即使多个侦听器不共享订阅,也允许多个侦听器共享一个连接和一个线程。因此,无论应用程序跟踪多少个侦听器或通道,运行时成本在其整个生命周期内保持不变。此外,容器允许更改运行时配置,以便您可以在应用程序运行时添加或删除侦听器,而无需重新启动。此外,容器使用延迟订阅方法,RedisConnection仅在需要时使用。如果所有侦听器都已取消订阅,则会自动执行清理,并释放该线程。

为了帮助消息的异步性,容器需要一个java.util.concurrent.Executor(或Spring TaskExecutor)来分派消息。根据负载,侦听器数量或运行时环境,您应该更改或调整执行程序以更好地满足您的需求。特别是,在托管环境(例如应用程序服务器)中,强烈建议选择适当的TaskExecutor方式来利用其运行时。

2.2 MessageListenerAdapter 

MessageListenerAdapter类是Spring的异步支持消息的最后一个组件。简而言之,它允许您将几乎任何类暴露为MDP(尽管存在一些约束)。请考虑以下接口定义:

public interface MessageDelegate {
  void handleMessage(String message);
  void handleMessage(Map message); void handleMessage(byte[] message);
  void handleMessage(Serializable message);
  // pass the channel/pattern as well
  void handleMessage(Serializable message, String channel);
 }

请注意,虽然接口不扩展MessageListener接口,但仍可以通过使用MessageListenerAdapter该类将其用作MDP 。还要注意如何使用各种消息处理方法是根据强类型的内容不同的Message,他们可以接收和处理类型。此外,发送消息的通道或模式可以作为类型的第二个参数传递给方法String

public class DefaultMessageDelegate implements MessageDelegate {
  // implementation elided for clarity...
}

注意上面的MessageDelegate接口实现(上面的DefaultMessageDelegate类)根本没有 Redis依赖。我们使用以下配置制将POJO转换为MDP:

<?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:redis="http://www.springframework.org/schema/redis"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/redis http://www.springframework.org/schema/redis/spring-redis.xsd">

<!-- the default ConnectionFactory -->
<redis:listener-container>
  <!-- the method attribute can be skipped as the default method name is "handleMessage" -->
  <redis:listener ref="listener" method="handleMessage" topic="chatroom" />
</redis:listener-container>

<bean id="listener" class="redisexample.DefaultMessageDelegate"/>
 ...
<beans>

注意:侦听器topic可以是通道(例如topic="chatroom")或模式(例如topic="*room"

前面的示例使用Redis命名空间声明消息侦听器容器并自动将POJO注册为侦听器。完整的bean定义如下:

<bean id="messageListener" class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter">
  <constructor-arg>
    <bean class="redisexample.DefaultMessageDelegate"/>
  </constructor-arg>
</bean>

<bean id="redisContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer">
  <property name="connectionFactory" ref="connectionFactory"/>
  <property name="messageListeners">
    <map>
      <entry key-ref="messageListener">
        <bean class="org.springframework.data.redis.listener.ChannelTopic">
          <constructor-arg value="chatroom">
        </bean>
      </entry>
    </map>
  </property>
</bean>

每次收到消息时,适配器都会自动透明地执行RedisSerializer低级格式和所需对象类型之间的转换(使用已配置)。由方法调用引起的任何异常都由容器捕获和处理(默认情况下,会记录异常)。

猜你喜欢

转载自blog.csdn.net/top_explore/article/details/94356245