文章目录
前言
通过redis的发布订阅功能实现消息队列的功能
一、Redis发布订阅相关命令
命令 | 说明 |
---|---|
PUBLISH channel message | 将信息 message 发送到指定的频道 channel 。 |
PSUBSCRIBE pattern [pattern ...] | 订阅一个或多个符合给定模式的频道。每个模式以 * 作为匹配符,比如 it* 匹配所有以 it 开头的频道 |
PUNSUBSCRIBE [pattern [pattern ...]] | 指示客户端退订所有给定模式。如果没有模式被指定,也即是,一个无参数的 PUNSUBSCRIBE 调用被执行,那么客户端使用 PSUBSCRIBE 命令订阅的所有模式都会被退订 |
SUBSCRIBE channel [channel ...] | 订阅给定的一个或多个频道的信息。 |
UNSUBSCRIBE [channel [channel ...]] | 指示客户端退订给定的频道。如果没有频道被指定,也即是,一个无参数的 UNSUBSCRIBE 调用被执行,那么客户端使用 SUBSCRIBE 命令订阅的所有频道都会被退订 |
PUBSUB | 1、PUBSUB CHANNELS [pattern] :列出当前的活跃频道。2、 PUBSUB NUMSUB [channel-1 ... channel-N] :返回给定频道的订阅者数量, 订阅模式的客户端不计算在内。 3、 PUBSUB NUMPAT 返回订阅模式的数量。注意, 这个命令返回的不是订阅模式的客户端的数量, 而是客户端订阅的所有模式的数量总和。 |
二、使用步骤
对下面用到的名称做下说明:
渠道:想当与队列名称
频道:分为普通频道和【符合给定模式】的频道。普通频道相当于Queue模式;【符合给定模式】的频道相当于Topic模式。
第一步、添加redis依赖包
stydu-base-0.0.1-SNAPSHOT.jar 是我自己学习封装的redis连接池。如果你们自己实现,需要改一下下面代码中关于获取redis连接的相关部分即可。
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.1.0</version>
</dependency>
若使用我这个jar包,必须使用 redis_config.properties
这个文件名做redis配置。
我再这个项目中配置的内容如下:
host=192.168.75.129
port=6379
password=123456
timeout=2000
setMaxTotal=8
maxIdle=8
第二步、编写一个订阅、取消订阅、发布消息的工具类RedisPubSubUtils
工具类包含以下方法:
publishMsg(String, String)
:发布消息到指定渠道
subscribeChannel(Jedis, JedisPubSub, String...)
:订阅给定的一个或多个频道的信息psubscribeChannel(Jedis, JedisPubSub, String...)
:订阅一个或多个【符合给定模式】的频道
unsubscribeChannel(JedisPubSub)
:取消通过subscribe(subscribeChannel方法)订阅的全部频道unsubscribeChannel(JedisPubSub, String...)
:取消订阅一个或多个频道
punsubscribeChannel(JedisPubSub)
:取消通过psubscribe(psubscribeChannel方法)订阅的全部频道punsubscribeChannel(JedisPubSub, String...)
:取消订阅一个或多个符合给定模式的频道
代码为:
package com.study.redis发布订阅;
import com.study.utils.RedisSimplePool;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
/**
*
* @描述: 发布与订阅工具类 ,取消订阅渠道需要在JedisPubSub中
* @版权: Copyright (c) 2020
* @公司:
* @作者: 严磊
* @版本: 1.0
* @创建日期: 2020年6月18日
* @创建时间: 上午11:04:08
*/
public class RedisPubSubUtils
{
/**
*
* @描述:发布消息到渠道
* @作者:严磊
* @时间:2020年6月18日 上午11:17:41
* @param channel
* @param message
*/
public static void publishMsg(String channel, String message) {
Jedis jedis = RedisSimplePool.getJedis();
jedis.publish(channel, message);
RedisSimplePool.returnJedis(jedis);
}
/**
*
* @描述:订阅给定的一个或多个频道的信息
* @作者:严磊
* @时间:2020年6月18日 上午11:27:15
* @param jedis
* @param jedisPubSub
* @param channels
*/
public static void subscribeChannel(Jedis jedis,JedisPubSub jedisPubSub, String... channels) {
jedis.subscribe(jedisPubSub, channels);
}
/**
*
* @描述:订阅一个或多个【符合给定模式】的频道
* @作者:严磊
* @时间:2020年6月18日 上午11:27:15
* @param jedis
* @param jedisPubSub
* @param channels
*/
public static void psubscribeChannel(Jedis jedis,JedisPubSub jedisPubSub, String... patterns) {
jedis.psubscribe(jedisPubSub, patterns);
}
/**
*
* @描述:取消通过subscribe(subscribeChannel方法)订阅的全部频道
* @作者:严磊
* @时间:2020年6月18日 上午11:27:15
* @param jedis
* @param jedisPubSub
* @param channels
*/
public static void unsubscribeChannel(JedisPubSub jedisPubSub) {
jedisPubSub.unsubscribe();
}
/**
*
* @描述:取消订阅一个或多个频道
* @作者:严磊
* @时间:2020年6月18日 上午11:27:15
* @param jedis
* @param jedisPubSub
* @param channels
*/
public static void unsubscribeChannel(JedisPubSub jedisPubSub, String... channels) {
jedisPubSub.unsubscribe(channels);
}
/**
*
* @描述:取消通过psubscribe(psubscribeChannel方法)订阅的全部频道
* @作者:严磊
* @时间:2020年6月18日 上午11:27:15
* @param jedis
* @param jedisPubSub
* @param channels
*/
public static void punsubscribeChannel(JedisPubSub jedisPubSub) {
jedisPubSub.punsubscribe();
}
/**
*
* @描述:取消订阅一个或多个符合给定模式的频道
* @作者:严磊
* @时间:2020年6月18日 上午11:27:15
* @param jedis
* @param jedisPubSub
* @param channels
*/
public static void punsubscribeChannel(JedisPubSub jedisPubSub, String... patterns) {
jedisPubSub.punsubscribe(patterns);
}
}
三、频道监听者RedisMsgSubListener(即消费者)
所有消费者都要继承
JedisPubSub
类。
我这里就只写了一个消费者。Queue模式和Topic模式都公用一个消费者
RedisMsgSubListener重写了JedisPubSub中的下列方法用于监听处理:
onMessage(String, String)
:取得订阅的消息后的处理[Queue模式]
onPMessage(String, String, String)
:取得按表达式的方式订阅的消息后的处理[Topic模式]
onSubscribe(String, int)
:初始化订阅时候的处理[Queue模式]
onPSubscribe(String, int)
:初始化按表达式的方式订阅时候的处理[Topic模式]
onUnsubscribe(String, int)
:取消订阅的时候的处理[Queue模式]
onPUnsubscribe(String, int)
:取消按表达式的方式订阅时候的处理[Topic模式]
onPong(String)
:测试连接
代码为:
package com.study.redis发布订阅;
import redis.clients.jedis.JedisPubSub;
/**
*
* @描述: 频道监听者
* @版权: Copyright (c) 2020
* @公司:
* @作者: 严磊
* @版本: 1.0
* @创建日期: 2020年6月18日
* @创建时间: 下午1:19:34
*/
public class RedisMsgSubListener extends JedisPubSub
{
/**
*
* @描述:取得订阅的消息后的处理
* @作者:严磊
* @时间:2020年6月18日 下午12:26:39
* @param channel
* @param message
*/
@Override
public void onMessage(String channel, String message)
{
System.out.println(Thread.currentThread().getName()+Thread.currentThread().getName()+"取得订阅的消息后的处理:" + channel + "=" + message);
//测试取消订阅
System.out.println("测试获取到消息后取消订阅:"+channel);
RedisPubSubUtils.unsubscribeChannel(this, channel);
}
/**
*
* @描述:取得按表达式的方式订阅的消息后的处理
* @作者:严磊
* @时间:2020年6月18日 下午12:26:50
* @param pattern
* @param channel
* @param message
*/
@Override
public void onPMessage(String pattern, String channel, String message)
{
System.out.println(Thread.currentThread().getName()+"取得按表达式的方式订阅的消息后的处理:" + pattern + "=" + channel + "=" + message);
//测试取消订阅
System.out.println("测试获取到消息后取消订阅:"+channel+":"+pattern);
RedisPubSubUtils.punsubscribeChannel(this, pattern);
}
/**
*
* @描述:初始化订阅时候的处理
* @作者:严磊
* @时间:2020年6月18日 下午12:27:01
* @param channel
* @param subscribedChannels 处于订阅状态频道数量
*/
@Override
public void onSubscribe(String channel, int subscribedChannels)
{
System.out.println(Thread.currentThread().getName()+"初始化订阅时候的处理:" + channel + "=" + subscribedChannels);
}
/**
*
* @描述:取消订阅时候的处理
* @作者:严磊
* @时间:2020年6月18日 下午12:27:08
* @param channel
* @param subscribedChannels 处于订阅状态频道数量
*/
@Override
public void onUnsubscribe(String channel, int subscribedChannels)
{
System.out.println(Thread.currentThread().getName()+"取消订阅时候的处理:" + channel + "=" + subscribedChannels);
}
/**
*
* @描述:初始化按表达式的方式订阅时候的处理
* @作者:严磊
* @时间:2020年6月18日 下午12:27:15
* @param pattern
* @param subscribedChannels 处于订阅状态频道数量
*/
@Override
public void onPSubscribe(String pattern, int subscribedChannels)
{
System.out.println(Thread.currentThread().getName()+"初始化按表达式的方式订阅时候的处理:" + pattern + "=" + subscribedChannels);
}
/**
*
* @描述:取消按表达式的方式订阅时候的处理
* @作者:严磊
* @时间:2020年6月18日 下午12:27:21
* @param pattern
* @param subscribedChannels 处于订阅状态频道数量
*/
@Override
public void onPUnsubscribe(String pattern, int subscribedChannels)
{
System.out.println(Thread.currentThread().getName()+"取消按表达式的方式订阅时候的处理:" + pattern + "=" + subscribedChannels);
}
@Override
public void onPong(String pattern)
{
System.out.println(Thread.currentThread().getName()+"onPong:" + pattern);
}
}
四、测试
1、测试Queue模式
消息生产者(发布消息到频道)
package com.study.redis发布订阅.testSub;
import com.study.redis发布订阅.RedisPubSubUtils;
import com.study.utils.RedisSimplePool;
import redis.clients.jedis.Jedis;
/**
*
* @描述: 发布信息到渠道
* @版权: Copyright (c) 2020
* @公司:
* @作者: 严磊
* @版本: 1.0
* @创建日期: 2020年6月18日
* @创建时间: 下午1:38:14
*/
public class PubTest
{
public static void main(String[] args)
{
Jedis jedis = RedisSimplePool.getJedis();
RedisPubSubUtils.publishMsg("test-channel-1", "hello world!");
RedisPubSubUtils.publishMsg("test-channel-2", "I miss you!");
RedisSimplePool.returnJedis(jedis);
}
}
消费者
package com.study.redis发布订阅.testSub;
import com.study.redis发布订阅.RedisMsgSubListener;
import com.study.redis发布订阅.RedisPubSubUtils;
import com.study.utils.RedisSimplePool;
import redis.clients.jedis.Jedis;
/**
*
* @描述: 两个线程分别都订阅test-channel-1、test-channel-2 这两个频道
* @版权: Copyright (c) 2020
* @公司:
* @作者: 严磊
* @版本: 1.0
* @创建日期: 2020年6月18日
* @创建时间: 下午1:34:44
*/
public class SubTest {
public static void main(String[] args) {
Runnable r = new Runnable()
{
@Override
public void run()
{
Jedis jedis = RedisSimplePool.getJedis();
RedisMsgSubListener listener = new RedisMsgSubListener();
RedisPubSubUtils.subscribeChannel(jedis, listener, "test-channel-1","test-channel-2");
System.out.println("会阻塞住-直到没有订阅的频道");
}
};
Thread t1 = new Thread(r,"订阅者线程Thread-1");
Thread t2 = new Thread(r,"订阅者线程Thread-2");
t1.start();
t2.start();
}
}
2、测试Topic模式
消息生产者(发布消息到频道)
package com.study.redis发布订阅.testPSub;
import com.study.redis发布订阅.RedisPubSubUtils;
import com.study.utils.RedisSimplePool;
import redis.clients.jedis.Jedis;
/**
*
* @描述: 发布信息到渠道
* @版权: Copyright (c) 2020
* @公司:
* @作者: 严磊
* @版本: 1.0
* @创建日期: 2020年6月18日
* @创建时间: 下午1:38:14
*/
public class PubTest
{
public static void main(String[] args)
{
Jedis jedis = RedisSimplePool.getJedis();
RedisPubSubUtils.publishMsg("aaaa-channel-1", "hello world!");
RedisPubSubUtils.publishMsg("bbbb-channel-2", "I miss you!");
RedisSimplePool.returnJedis(jedis);
}
}
消费者
package com.study.redis发布订阅.testPSub;
import com.study.redis发布订阅.RedisMsgSubListener;
import com.study.redis发布订阅.RedisPubSubUtils;
import com.study.utils.RedisSimplePool;
import redis.clients.jedis.Jedis;
/**
*
* @描述: 两个线程分别都订阅test-channel-1、test-channel-2 这两个频道
* @版权: Copyright (c) 2020
* @公司:
* @作者: 严磊
* @版本: 1.0
* @创建日期: 2020年6月18日
* @创建时间: 下午1:34:44
*/
public class SubTest {
public static void main(String[] args) {
Runnable r = new Runnable()
{
@Override
public void run()
{
Jedis jedis = RedisSimplePool.getJedis();
RedisMsgSubListener listener = new RedisMsgSubListener();
RedisPubSubUtils.psubscribeChannel(jedis, listener, "aaaa-*","bbbb-*","cccc-*");
System.out.println("会阻塞住");
}
};
Thread t1 = new Thread(r,"订阅者线程Thread-1");
Thread t2 = new Thread(r,"订阅者线程Thread-2");
t1.start();
t2.start();
}
}
五、项目Git地址
study-redis