使用线程监听Redis多个频道及多个数据类型

1、说明:
使用手动创建线程的方式,对应的一个线程监听一个频道或者数据类型,而对应new 出来的线程对象放在了map中,根据key为频道名称或数据类型名称,value为线程对象。
此方式为了随时可以手动启动关闭监听对应的频道及数据类型,当然也可使用线程池来管理频道的监听,redis频道具备订阅与取消订阅的功能,所以可以通过取消订阅来做到不再监听指定频道。而数据类型则无法做到,所以需要通过手动终止线程的方式来停止对指定数据类型的监听。
2、编码
2.1、首先创建一个类,继承Thread类并且实现她的run方法

import com.ruoyi.utils.RedisConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;

/***
 * 监听redis频道的线程类
 */
public class RedisTopicThread extends Thread{
    private Logger logger= LoggerFactory.getLogger(RedisTopicThread.class);

    //频道监听器
    private Subscriber subscriber;

    //频道名称
    private  String channel;

    //redis连接配置
    private RedisConfig redisConfig;


    public RedisTopicThread(Subscriber subscriber, String channelName, RedisConfig redisConfig) {
        super();
        this.subscriber = subscriber;
        this.channel = channelName;
        this.redisConfig=redisConfig;
    }

    /***
     * 启动线程
     */
    public synchronized void run() {
        Jedis jedis = null;
        try {
            if(this.interrupted()){    //如果当前线程处于停止状态,抛出异常
                throw new InterruptedException();
            }else{
                //jedis = RedisConnect.redis(ip,port,pass);   //取出一个连接
                jedis=redisConfig.redis();
                jedis.subscribe(subscriber, channel);    //通过subscribe 的api去订阅,入参是订阅者和频道名
            }

        } catch (Exception e) {
            //捕捉抛出的异常终止线程
            logger.info("终止线程!"+e.toString());
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }


}

2.2、创建一个类型继承JedisPubSub类,重写其onMessage(),onSubscribe(),onUnsubscribe()方法,如下

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.domain.RedisTopic;
import com.ruoyi.service.RedisTopicService;
import com.ruoyi.web.utils.MongodbClient;
import com.ruoyi.utils.RedisConfig;
import com.ruoyi.web.utils.MongodbConfig;
import com.ruoyi.web.utils.WebSocketServer;
import redis.clients.jedis.JedisPubSub;

import java.util.Date;


public class Subscriber extends JedisPubSub {
    private String subName;
    private RedisConfig redisConfig;
    private RedisTopic redisTopic;
    private RedisTopicService redisTopicService;
    private MongodbConfig mongodbConfig;
    public Subscriber(RedisConfig redisConfig,String subName,RedisTopic redisTopic,RedisTopicService redisTopicService,MongodbConfig mongodbConfig){
        this.subName = subName;
        this.redisConfig=redisConfig;
        this.redisTopic=redisTopic;
        this.redisTopicService=redisTopicService;
        this.mongodbConfig=mongodbConfig;
    }

    /**
     * 订阅后收到消息触发
     * @param channel
     * @param message
     */
    public void onMessage(String channel, String message) {
        JSONObject jsonObject=new JSONObject();
        JSONObject jsonObject1=new JSONObject();
        try {
            redisTopic.setRecord(message);
            redisTopicService.updateTopicRecordByName(redisTopic);
            jsonObject.put("time",new Date().getTime());
            jsonObject.put("message",message);
            jsonObject.put("topic",channel);
            new MongodbClient(mongodbConfig).insertInfo(channel,jsonObject);
            redisConfig.redis().lpush(channel,jsonObject.toString());
            jsonObject1.put("type","message");
            jsonObject1.put("value",jsonObject);
            jsonObject1.put("topic",channel);
            WebSocketServer.sendInfo(jsonObject1.toString(),"topicData");
            WebSocketServer.sendInfo(jsonObject1.toString(),"main");
            WebSocketServer.sendInfo("topicOnMessage","redisPage");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /***
     * 订阅频道
     * @param channel
     * @param subscribedChannels
     */
    public void onSubscribe(String channel, int subscribedChannels) {
        System.out.println("已成功订阅"+channel+"频道,subscribedChannels:"+subscribedChannels);
    }

    /**
     * 取消订阅
     * @param channel
     * @param subscribedChannels
     */
    public void onUnsubscribe(String channel, int subscribedChannels) {
        System.out.println("已取消订阅"+channel+"频道,subscribedChannels:"+subscribedChannels);

    }
}

2.3、定义两个map用于存放线程对象及频道监听器对象

    //用于存放频道监听线程对象
    Map<String, RedisTopicThread> map=new HashMap<>();
    //用于存放频道监听器对象
    Map<String,Subscriber>map1=new HashMap<>();

2.4、开始监听指定频道及关闭监听指定频道
2.4.1、开始监听

  /**
     * 开始监听指定频道
     * @return
     */
    @Log(title = "Redis监听配置,开启频道监听",businessType = BusinessType.UPDATE)
    @RequestMapping("/startListen")
    @ResponseBody
    public AjaxResult listen(@RequestParam(value = "topic",required = false)String topic){
        Integer result = null;
        try {
                    RedisTopic redisTopic=redisTopicService.findTopicInfoByName(topic);

                    //将未被监听的频道修改为监听状态
                    redisTopicService.updateTopicStatusByTopicName(1,topic);

                    //注册监听器
                    Subscriber subscriber = new Subscriber(redisConfig,topic,redisTopic,redisTopicService,mongodbConfig);

                    //状态为0,不在监听状态,重新创建一个线程来进行监听
                    RedisTopicThread redisTopicThread =new RedisTopicThread(subscriber,topic,redisConfig);
                    redisTopicThread.start();

                    //将线程放到map中
                    map.put(topic, redisTopicThread);

                    //将注册的监听器放到map中
                    map1.put(topic,subscriber);

                    //threadPoolTest.startTopicThread(subscriber,topic,redisConfig);

                    map1.put(topic,subscriber);

                    //将监听频道列表变化通知给前端
                    WebSocketServer.sendInfo("listenTopic","redisPage");
                    result=1;
        }catch (Exception e){
            result=0;
            e.printStackTrace();
        }
        return toAjax(result);
    }

2.4.2、停止监听,根据传过来的频道名称到map中找到对应的监听器对象取消其订阅,找到对应的线程对象进行终止。并将map中key为指定频道名称的数据移除掉

    /***
     * 停止监听告警频道
     * @return
     */
    @Log(title = "Redis监听配置,停止频道监听",businessType = BusinessType.UPDATE)
    @RequestMapping("/stopListen")
    @ResponseBody
    public AjaxResult stopListen(@RequestParam(value = "topic",required = false)String topic){
        Integer result;
        try {
            //修改表中的指定频道的监听状态
            redisTopicService.updateTopicStatusByTopicName(0,topic);

            //根据监听的频道得到对应的线程
            RedisTopicThread redisTopicThread =map.get(topic);

            //根据频道名称获取注册的监听器
            Subscriber subscriber=map1.get(topic);

            //根据频道名称移除map中的数据
            map.remove(topic);

            //移除指定的监听器
            map1.remove(topic);

            //取消订阅指定频道
            subscriber.unsubscribe(topic);

            //将当前线程标记为终止状态
            redisTopicThread.interrupt();

            //将监听频道列表变化通知给前端
            WebSocketServer.sendInfo("listenTopic","redisPage");
            result=1;
        }catch (Exception e){
            result=0;
            e.printStackTrace();
        }
        return toAjax(result);
    }

3、效果
启动监听四个频道

往频道发送信息后,触发onMessage()方法后通过,websocket来推送监听到的数据至前端
在这里插入图片描述
以上为频道监听的方式

数据类型的监听方式与此类似,下面就不作复述。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值