一.Redis 安装

     redis安装可参考:http://www.runoob.com/redis/redis-install.html

二. 简介

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

Redis 客户端可以订阅任意数量的频道。

下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

redis的subscribe与psubscribe区别 redis publish/subscribe_java

当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

redis的subscribe与psubscribe区别 redis publish/subscribe_Redis_02

三.基于java的redis使用示例

    1.常量类

package redistest.npersistent;

public class Constants {
	
	public static final String HOST = "127.0.0.1";// 本地服务器地址
	public static final int PORT = 6379;// 端口
	
	public static final String CHANNEL = "message-channel";// 消息通道(频道)
}

2.消息发布端类

package redistest.npersistent;

import redis.clients.jedis.Jedis;

/**
 * 消息发布端
 * 
 * @version 1.0
 * @since JDK1.7
 * @date 2017年9月6日 下午1:06:55
 */
public class PubClient {
	
	private Jedis jedis;
	
	// 连接redis服务器
    public PubClient(String host, int port){  
        jedis = new Jedis(host, port);  
    }
    
    /**
     * 
     * 消息发布
     *
     * @param channel 消息通道
     * @param message 消息体
     * 
     * @date 2017年9月8日 上午10:09:43
     */
    public void pub(String channel, String message){  
        jedis.publish(channel, message);
    }
      
    public void close(String channel){  
        jedis.publish(channel, "quit");  
        jedis.del(channel);  
    }
}

3.消息订阅端类

package redistest.npersistent;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;

/**
 * 
 * 消息订阅端
 * 
 * @version 1.0
 * @since JDK1.7
 * @date 2017年9月6日 下午1:19:21
 */
public class SubClient {

	private Jedis jedis;
    
    public SubClient(String host, int port){  
        jedis = new Jedis(host,port);  
    }  
    
    /**
     * 
     * 消息订阅
     * 	
     * @param listener jedisPubSub用于处理监听到的消息
     * @param channel  要订阅的消息通道
     * 
     * @date 2017年9月8日 上午10:49:06
     */
    public void sub(JedisPubSub listener,String channel){  
        jedis.subscribe(listener, channel);  // 订阅给定的一个或多个频道的信息
        
        //此处将会阻塞,在client代码级别为JedisPubSub在处理消息时,将会“独占”链接  
        //并且采取了while循环的方式,侦听订阅的消息 
        //
    }
}

4.订阅者消息处理器类

package redistest.npersistent;

import java.util.Date;

import common.DateUtil;
import redis.clients.jedis.JedisPubSub;

/**
 * 
 * 订阅者消息处理器
 * 1).要使用Jedis的Publish/Subscribe(发布/订阅)功能,必须编写对JedisPubSub的自己的实现
 * 2).Redis为我们提供了publish/subscribe(发布/订阅)功能。我们可以对某个channel(频道)进行subscribe(订阅),当有人在这个channel上publish(发布)消息时,
 *    redis就会通知我们,这样我们可以收到别人发布的消息。
 * 3).
 * 
 * @version 1.0
 * @since JDK1.7
 * @date 2017年9月6日 下午1:23:17
 */
public class PrintListener extends JedisPubSub {

	/**
	 * 取得订阅的消息后的处理
	 * 即:订阅得到的信息将会在lister的onMessage(…)方法中进行处理 
	 * 
	 * @param channel 
	 */
	@Override
	public void onMessage(String channel, String message) {
		
		// 接收到订阅频道消息后,业务处理逻辑
		// 此处对消息的处理只是简单的打印
		String time = DateUtil.dateToString(new Date(), "yyyy-MM-dd HH:mm:ss:SSS");
        System.out.println("message receive:" + message + ",channel:" + channel + "..." + time);  
        
        // 此处我们可以取消订阅 ,即如果从通道中接收到消息“quit” ,就取消订阅
        if(message.equalsIgnoreCase("quit")){
            this.unsubscribe(channel);  // 取消频道订阅
            System.out.println("取消频道订阅成功.......");
        }
	}
}

5.非持久化订阅测试

package redistest.npersistent;

import org.apache.commons.lang3.RandomStringUtils;

import redis.clients.jedis.JedisPubSub;

/**
 * 
 * 非持久化订阅测试
 * ===========================================================================================================================
 * 一个Redis client发布消息,其他多个redis client订阅消息,发布的消息“即发即失”,redis不会持久保存发布的消息;
 * 消息订阅者也将只能得到订阅之后的消息,通道中此前的消息将无从获得。
 * ===========================================================================================================================
 * 
 * @version 1.0
 * @since JDK1.7
 * @date 2017年9月6日 下午1:31:39
 */
public class PubSubTest {
	
	public static void main(String[] args) throws Exception{  
		
		// 连接到redis服务器
        PubClient pubClient = new PubClient(Constants.HOST, Constants.PORT);  // 消息发布端
        
        // 往消息通道中发布消息
        pubClient.pub(Constants.CHANNEL, "订阅之前发送的消息:我是张三丰......."); 
        pubClient.pub(Constants.CHANNEL, "订阅之前发送的消息:嘻嘻嘻嘻嘻.......");  
        Thread.sleep(2000);
        
        //消息订阅者非常特殊,需要独占链接,因此我们需要为它创建新的链接;  
        //此外,jedis客户端的实现也保证了“链接独占”的特性,sub方法将一直阻塞,  
        //直到调用listener.unsubscribe方法 
        Thread subThread = null;
        for (int i = 0; i < 3; i++) {
        	subThread = new Thread(new Runnable() {  
                @Override  
                public void run() {  
                    try{
                    	// 消息订阅端
                        SubClient subClient = new SubClient(Constants.HOST, Constants.PORT);
                        
                        System.out.println("----------订阅开始-------");  
                        
                        JedisPubSub listener = new PrintListener();// 订阅者消息处理器
                        
                        // 在API级别,此处为【轮询操作】,直到unsubscribe调用,才会返回 
                        subClient.sub(listener, Constants.CHANNEL);  
                        
                        System.out.println("----------订阅结束-------");  
                    }catch(Exception e){
                        e.printStackTrace();  
                    }
                }  
            });  
            subThread.start();
        }
        
        int i = 0;  
        while (i < 10) {  
            String message = RandomStringUtils.random(6, true, true);//apache-commons
            System.out.println("随机生成的字符串:" + message);
            pubClient.pub(Constants.CHANNEL, message);  
            i++;  
            Thread.sleep(1000);  
        }
        
        // 被动关闭指示,如果通道中,消息发布者确定通道需要关闭,那么就发送一个“quit”  
        // 那么在listener.onMessage()中接收到“quit”时,其他订阅client将执行“unsubscribe”操作。  
        pubClient.close(Constants.CHANNEL);  
        // 此外,你还可以这样取消订阅  
        // listener.unsubscribe(channel);
    }
}