一:什么是订阅发布:

什么是redis的发布订阅(pub/sub)?   Pub/Sub功能(means Publish, Subscribe)即发布及订阅功能。基于事件的系统中,Pub/Sub是目前广泛使用的通信模型,它采用事件作为基本的通信机制,提供大规模系统所要求的松散耦合的交互模式:订阅者(如客户端)以事件订阅的方式表达出它有兴趣接收的一个事件或一类事件;发布者(如服务器)可将订阅者感兴趣的事件随时通知相关订阅者。熟悉设计模式的朋友应该了解这与23种设计模式中的观察者模式极为相似。 

     同样,Redis的pub/sub是一种消息通信模式,主要的目的是解除消息发布者和消息订阅者之间的耦合,  Redis作为一个pub/sub的server, 在订阅者和发布者之间起到了消息路由的功能。

  简单来讲,这里面还有个channel的概念,这里就是频道的意思,比如你订阅了银行的频道,当你的资金发生变动时,银行就会通过它的频道给你发送信息,在这里,你是属于被动接收的,而不是向银行索要信息,这个例子中,你就是sub(订阅者),而银行就是pub(发布者)。

二、运用场景:

一直都认为会一样技术之前,都必须先明白这样一种技术在哪些地方会被用到,不能盲目的学东西。

  看到发布订阅的特性,用来做一个简单的实时聊天系统再适合不过了。这是其中之一,当然这样的东西,我们开发中很少涉及到。再举一个常用的,在我们的分布式架构中,常常会遇到读写分离的场景,在写入的过程中,就可以使用redis发布订阅,使得写入值及时发布到各个读的程序中,就保证数据的完整一致性。再比如,在一个博客网站中,有100个粉丝订阅了你,当你发布新文章,就可以推送消息给粉丝们拉。总之场景很多,需要去挖掘。

三、回顾java如何操作redis:

redis是一种缓存数据库,它也是C/S的结构,也就是客户端和服务端,一般来说,在java中,我们通常使用 jedis(客户端)去操作redis(服务端),这其中操作的时候,两者之间肯定要建立连接,就像数据库链接一样,在关系型数据库中,我们一般都维护一个连接池,以达到链接的复用,来省去建立连接和关闭连接的时间。所以在jedis中,同样也存在一个jedispool(jedis连接池)的概念,我们都是从池中去取连接使用。

四、代码实例:

需要导入的jar包:

redis java 订阅 java redis消息订阅与发布 场景_Redis发布订阅

1、redis的properties配置文件

#***************** jedis configuration *********************
#redis ip
redis.ip=127.0.0.1
#redis port
redis.port=6379
#redis passWord
redis.passWord=123456
#over time setup
redis.timeout=3000
#************************ jedis pool configuration *******************
#jedis Maximum number of active connections
jedis.pool.maxActive=100
#jedis Maximum number of free connections
jedis.pool.maxIdle=50
#When the jedis pool returns without a connection object, it waits for the maximum time, in milliseconds, for a connection to be available.
#If the wait time is exceeded, a JedisConnectionException is thrown directly
jedis.pool.maxWait=1500
# When retrieving a connection from the pool, check whether it is valid
jedis.pool.testOnBorrow=true
# Check for validity when returning the connection
jedis.pool.testOnReturn=true

2、读取配置文件工具类

package com.li.util;

import java.io.IOException;
import java.util.Properties;

public class DataSourceUtil {
	
	private static Properties properties = new Properties();
	
	static {
		try {
			//加载properties配置文件
			properties.load(DataSourceUtil.class.getResourceAsStream("/redis.properties"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.out.println("properties文件加载报错");
			e.printStackTrace();
		}
	}
	public static String getKey(String key) {
		return properties.getProperty(key);
	}

}

3、redis连接池 

package com.li.util;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class RedisPoolUtil {
	/**
	 * Jedis连接池使用步骤如下:
		1->获取Jedis实例需要从JedisPool中获取;
		2->用完Jedis实例需要返还给JedisPool;
		3->如果Jedis在使用过程中出错,则也需要还给JedisPool;
	 */
	
	private static JedisPool jedisPool = null;

	//不允许通过new创建该类的实例
	private RedisPoolUtil() {
		
	}
	
	//初始化redis连接池
	public static void initalPool() {
		//创建JedisPool连接池实例
		JedisPoolConfig config = new JedisPoolConfig();
		//设置连接池各项配置
		//1、可用连接实例的最大数目,如果赋值为-1,表示不限制.
		config.setMaxTotal(Integer.parseInt(DataSourceUtil.getKey("jedis.pool.maxActive")));
		//2、控制一个Pool最多有多少个状态为idle(空闲的)jedis实例,默认值也是8
		config.setMaxIdle(Integer.parseInt(DataSourceUtil.getKey("jedis.pool.maxIdle")));
		//3、等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时/如果超过等待时间,则直接抛出异常
		config.setMaxWaitMillis(Long.valueOf(DataSourceUtil.getKey("jedis.pool.maxWait")));
		//4、从池中获取连接的时候,是否进行有效检查
		config.setTestOnBorrow(Boolean.valueOf(DataSourceUtil.getKey("jedis.pool.testOnBorrow")));
		//5、归还连接的时候,是否进行有效检查
		config.setTestOnReturn(Boolean.valueOf(DataSourceUtil.getKey("jedis.pool.testOnReturn")));
		
		String ip = DataSourceUtil.getKey("redis.ip");
		int port = Integer.valueOf(DataSourceUtil.getKey("redis.port"));
		int timeout = Integer.valueOf(DataSourceUtil.getKey("redis.timeout"));
		String password = DataSourceUtil.getKey("redis.passWord");
		//根据配置实例化jedis池
		jedisPool = new JedisPool(config,ip,port,timeout,password);
		
	}
	
	//获得连接
	public static Jedis getConnect() {
		Jedis jedis = null;
		try {
			if(jedisPool != null) {
				jedis = jedisPool.getResource();
				return jedis;
			}else {
				System.out.println("jedisPool连接池为null");
				return null;
			}
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println(e);
			return null;
		}
	}
	//归还连接
	public static void closeConnect(Jedis jedis){
	    if(jedis!=null){
		    jedis.close();
		}
	}
	//关闭连接池
	public static void closePool() {
		if(jedisPool != null) {
			jedisPool.close();
		}
	}
   
	
}

4、redis发布者

package com.li.day1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/***
 * redis发布订阅---发布
 * @author Administrator
 *
 */
public class Publisher extends Thread{

	private final Jedis jedis;
	private final String channel;
	public Publisher(Jedis jedis,String channel) {
		super();
		this.jedis = jedis;
		this.channel = channel;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
		//一直监听控制台的输入,并发布到redis服务端
		while(true) {
			String message = null;
			try {
				//获取本地输入数据
				message = reader.readLine();
				System.out.println(channel+"="+message);
				//判断是否结束,是则关闭连接,否则发布数据
				if(!"quit".equals(message)) {
					jedis.publish(channel, message);
				}else {
					break;
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		
	}
	
	
}

5、redis发布订阅消息监听器

package com.li.day1;

import redis.clients.jedis.JedisPubSub;

/***
 * redis发布订阅---客户端订阅监听
 * redis发布订阅消息监听器
 * @author Administrator
 *
 */
public class RedisMsgPubSubListener  extends JedisPubSub{
	/*
	 * 监听到订阅频道接受到消息时的回调 
	 */
	@Override
	public void onMessage(String channel, String message) {
		// TODO Auto-generated method stub
		System.out.println(" 监听到订阅频道接受到消息时的回调 :"+channel+" : "+message);
		super.onMessage(channel, message);
	}
	/*
	 * 监听到订阅模式接受到消息时的回调
	 */
	@Override
	public void onPMessage(String pattern, String channel, String message) {
		// TODO Auto-generated method stub
		System.out.println("监听到订阅模式接受到消息时的回调 : "+channel+" : "+message);
		super.onPMessage(pattern, channel, message);
	}
	/*
	 * 订阅频道时的回调
	 */
	@Override
	public void onPSubscribe(String pattern, int subscribedChannels) {
		// TODO Auto-generated method stub
		System.out.println("订阅频道时的回调 :"+pattern+" : "+subscribedChannels);
		super.onPSubscribe(pattern, subscribedChannels);
	}
	/*
	 * 取消订阅频道时的回调
	 */
	@Override
	public void onPUnsubscribe(String pattern, int subscribedChannels) {
		// TODO Auto-generated method stub
		System.out.println("取消订阅频道时的回调 :"+pattern+" : "+subscribedChannels);
		super.onPUnsubscribe(pattern, subscribedChannels);
	}
	/*
	 * 订阅频道模式时的回调
	 */
	@Override
	public void onSubscribe(String channel, int subscribedChannels) {
		// TODO Auto-generated method stub
		System.out.println("订阅模式时的回调: "+channel+":"+subscribedChannels);
		super.onSubscribe(channel, subscribedChannels);
	}
	/*
	 * 取消订阅模式时的回调
	 */
	@Override
	public void onUnsubscribe(String channel, int subscribedChannels) {
		// TODO Auto-generated method stub
		System.out.println(" 取消订阅模式时的回调: "+channel+":"+subscribedChannels);
		super.onUnsubscribe(channel, subscribedChannels);
	}
	
}

6、redis订阅者

package com.li.day1;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/***
 * redis发布订阅---订阅
 * @author Administrator
 *
 */
public class Subscriber extends Thread{
	private final Jedis jedis;
	private final String channel;
	
	
	public Subscriber(Jedis jedis, String channel) {
		super();
		this.jedis = jedis;
		this.channel = channel;
	}


	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			//创建redis发布订阅消息监听器
			RedisMsgPubSubListener redisMsgPubSubListener = new RedisMsgPubSubListener();
			//通过监听器的api去订阅,入参是订阅者和频道名
			jedis.subscribe(redisMsgPubSubListener, channel);
		}catch (Exception e) {
			System.out.println("订阅消息报错:"+e);
			// TODO: handle exception
		} finally {
			// TODO: handle finally clause
			if(jedis != null) {
				jedis.close();
			}
		}
		
	}
	
	
	
}

7、测试类

package com.li.day1;

import com.li.util.RedisPoolUtil;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class PubSubMain {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//频道
		String channel = "myChannel";
		//初始化redisPool连接池
		RedisPoolUtil.initalPool();
		//订阅者
		Subscriber Subscriber = new Subscriber(RedisPoolUtil.getConnect(), channel);	
		Subscriber.start();
		//发布者
		Publisher publisher = new Publisher(RedisPoolUtil.getConnect(),channel);
		publisher.start();
	}

}

8、测试结果

redis java 订阅 java redis消息订阅与发布 场景_System_02