以前使用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收到清除緩存的消息 (訂閱者收到消息),更新了推薦板塊的緩存。