发布订阅(pub/sub)是一种消息通信模式,主要的目的是解耦消息发布者和消息订阅者之间的耦合,这点和设计模式中的观察者模式比较相似。pub /sub不仅仅解决发布者和订阅者直接代码级别耦合也解决两者在物理部署上的耦合。redis作为一个pub/sub server,在订阅者和发布者之间起到了消息路由的功能。订阅者可以通过subscribe和psubscribe命令向redis server订阅自己感兴趣的消息类型,redis将消息类型称为通道(channel)。当发布者通过publish命令向redis server发送特定类型的消息时。订阅该消息类型的全部client都会收到此消息。这里消息的传递是多对多的。一个client可以订阅多个 channel,也可以向多个channel发送消息。
初次使用redis的消息订阅发布,在网上搜了一圈发现都是比较笨的在配置文件配置消息的形式,自己折腾着换了种方法实现,贴出来自己记录一下。
先在maven引入对应的jar,注意版本问题,版本不对应会出很多奇奇怪怪的问题。
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.2.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.2</version>
</dependency>
然后是redis的配置文件redis-context.xml。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.zavfwei.redis" />
<!-- scanner redis properties -->
<context:property-placeholder location="classpath:config/redis.properties" file-encoding="utf-8" ignore-unresolvable="true"/>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${cache.redis.maxIdle}" />
<property name="maxTotal" value="${cache.redis.maxTotal}" />
<property name="testOnBorrow" value="${cache.redis.testOnBorrow}" />
</bean>
<bean id="redisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="usePool" value="true"></property>
<property name="hostName" value="${cache.redis.host}" />
<property name="port" value="${cache.redis.port}" />
<property name="password" value="${cache.redis.password}" />
<property name="timeout" value="${cache.redis.timeout}" />
<property name="database" value="${cache.redis.db}"></property>
<constructor-arg index="0" ref="jedisPoolConfig" />
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="redisConnectionFactory" />
<property name="keySerializer" ref="stringRedisSerializer" />
<property name="valueSerializer" ref="stringRedisSerializer" />
<property name="hashKeySerializer" ref="stringRedisSerializer" />
<property name="hashValueSerializer" ref="stringRedisSerializer" />
</bean>
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="redisConnectionFactory" />
<property name="keySerializer" ref="stringRedisSerializer" />
<property name="valueSerializer" ref="stringRedisSerializer" />
<property name="hashKeySerializer" ref="stringRedisSerializer" />
<property name="hashValueSerializer" ref="stringRedisSerializer" />
</bean>
<bean id="stringRedisSerializer"
class="org.springframework.data.redis.serializer.StringRedisSerializer" >
</bean>
</beans>
消息订阅发布的一个工具类RedisCacheDao。
package com.zavfwei.redis.core;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import javax.annotation.Resource;
/**
* Created by winton on 2017/3/21 0021.
*/
@Service
public class RedisCacheDao<T> {
@Resource(name = "redisTemplate")
public RedisTemplate redisTemplate;
/**
* 发布消息到指定的频道
*
* @param channel
* @param message
*/
public void publish(final String channel, final String message) {
redisTemplate.execute(new RedisCallback<Object>() {
public Object doInRedis(final RedisConnection connection)
throws DataAccessException {
((Jedis) connection.getNativeConnection()).publish(channel, message);
return null;
}
});
}
/**
* 订阅给定的一个或多个频道的信息
*
* @param jedisPubSub
* @param channels
*/
public void subscribe(final JedisPubSub jedisPubSub, final String... channels) {
redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
((Jedis) connection.getNativeConnection()).subscribe(jedisPubSub, channels);
return null;
}
});
}
}
测试消息的订阅发布类。
package com.zavfwei.service.other;
import com.zavfwei.RedisKey;
import com.zavfwei.redis.core.RedisCacheDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisPubSub;
import javax.annotation.PostConstruct;
/**
* Created by winton on 2017/3/15 0015.
*/
@Service
public class WithdrawService {
@Autowired
private RedisCacheDao redisCacheDao;
private Logger log = LoggerFactory.getLogger(this.getClass());
@PostConstruct
public void init(){
new Thread(){
@Override
public void run() {
redisCacheDao.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
log.debug("redis通知,channel={},message={}",channel,message);
if(channel.equals(RedisKey.TXAPPLY_CHANNEL)){
}
}
},RedisKey.TXAPPLY_CHANNEL);
}
}.start();
}
public void publish(){
redisCacheDao.publish(RedisKey.TXAPPLY_CHANNEL,"massage");
}
}