对Redis发布订阅功能使用Demo


前言

通过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 命令订阅的所有频道都会被退订
PUBSUB1、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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值