以前使用redis只是用来当做非关系型数据库来使用,提高查询的效率。最近使用到了redis的一个新功能,redis的发布订阅模式。
“发布/订阅”(publish/subscribe)模式可以实现进程间通信,订阅者可以订阅一个或多个频道(channel),而发布者可以向指定的频道发送消息,所有订阅此频道的订阅者都会收到此消息并执行相应的操作。
redis使用RedisMessageListenerContainer进行消息监听,客户程序需要自己实现MessageListener,并以指定的topic注册到RedisMessageListenerContainer,这样,在指定的topic上如果有消息,RedisMessageListenerContainer便会通知该MessageListener。下边演示在ssm框架下如何使用redis进行消息的发布/订阅。
1、spring配置文件中的配置
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd ">
<!-- jedis pool配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.maxActive}"/>
<property name="maxIdle" value="${redis.maxIdle}"/>
<property name="maxWaitMillis" value="${redis.maxWait}"/>
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
</bean>
<!-- spring data redis -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="poolConfig" ref="jedisPoolConfig"></property>
<property name="hostName" value="${redis.host}"></property>
<property name="port" value="${redis.port}"></property>
<property name="timeout" value="${redis.timeout}"></property>
<property name="usePool" value="${redis.usePool}"></property>
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
</bean>
<!-- 消息接受类 -->
<bean id="redisTestMessageListener" class="com.dongao.job.service.impl.RedisTestMessageListener"></bean>
<bean id="redisContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer">
<property name="connectionFactory" ref="jedisConnectionFactory" />
<property name="taskExecutor"><!-- 此处有个奇怪的问题,无法正确使用其他类型的Executor -->
<bean class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
<property name="poolSize" value="2"></property>
</bean>
</property>
<property name="messageListeners">
<map>
<entry key-ref="redisTestMessageListener">
<list>
<!-- 普通订阅,订阅具体的频道 -->
<bean class="org.springframework.data.redis.listener.ChannelTopic">
<constructor-arg value="${redis.test.channel1}" />
</bean>
<bean class="org.springframework.data.redis.listener.ChannelTopic">
<constructor-arg value="${redis.test.channel2}" />
</bean>
</list>
</entry>
</map>
</property>
</bean>
</beans>
配置文件中配置的是订阅了两个频道。如果是3个,上边的poolSize 配置为3,相应的list中的bean配置为三个。
2、发布消息的两个方法(频道):
@Controller
@RequestMapping(value="demo")
public class DemoController {
//注入redis客户端jedis
/*@Autowired
private ShardJedisUtil jedisUtil;*/
@SuppressWarnings("rawtypes")
@Resource(name="redisTemplate")
private RedisTemplate redisTemplate;
@RequestMapping("/test")
@ResponseBody
public String test1(){
String str="消息1";
redisTemplate.convertAndSend(Config.redis_channel1, str);
return "消息1发送成功!";
}
@RequestMapping("/test")
@ResponseBody
public String test2(){
String str2="消息2";
redisTemplate.convertAndSend(Config.redis_channel2, str2);
return "消息2发送成功!";
}
}
3、接收消息,执行操作的方法类:
@Component
public class RedisTestMessageListener implements MessageListener {
@Override
public void onMessage(Message message, byte[] pattern) {
System.out.println("接受消息:"+message.toString());
}
}
message中包含发送消息的频道和消息的内容,这个时候运行的结果会直接显示乱码:
把接受消息的方法类修改一下,把message中的内容进行序列化,就可以显示频道和内容了:
@Component
public class RedisTestMessageListener implements MessageListener {
@SuppressWarnings("rawtypes")
@Resource(name="redisTemplate")
private RedisTemplate redisTemplate;
@Override
public void onMessage(Message message, byte[] pattern) {
String topic = (String)redisTemplate.getStringSerializer().deserialize(message.getChannel());
Object body = redisTemplate.getValueSerializer().deserialize(message.getBody());
String msg = String.valueOf(body);
System.out.println(topic);
System.out.println("接受消息:"+msg);
}
}
显示结果:
以上就是redis发布/订阅的简单使用,举个简单的应用场景的话,以门户网站为例, 当编辑更新了某推荐板块的内容后:CMS发布 清除缓存的消息到channel (推送者推送消息)门户网站的缓存系统通过channel收到 清除缓存的消息 (订阅者收到消息),更新了推荐板块的缓存。