在springboot中使用redis的pubsub进行消息的订阅发布

近日遇到需要发消息的情况,脑中直接浮现了kafka和emq(rabbitMQ)工具。

但是我仔细一想,发现了问题:

1.这次需要的消息功能非常简单,用量也不大

2.服务器的内存和硬盘都很小,还是单节点,kafka和mq这种会对服务器造成压力,而且有大材小用的意思

3.忽然间想起了redis也有消息队列的功能。

查询了一下,决定使用jedis的JedisPubSub。

和网上的例子不同的是,他们的是在服务器启动的时候直接订阅了channel,再也不会改变。

而我需要的是在用户登陆的时候,每个用户订阅属于自己特定的channel。

主要的过程是:

1.登陆后传入userid,使用jedis.subscribe订阅特定的channel;

2.使用JedisPubSub的onMessage接收消息;

解决的难点有:

1.jedis的pub和sub是阻塞的(例如onsubscribe),为此需要专门开辟线程进行处理

2.登陆后,订阅channel后,退出登陆,再次登陆,这时会重复订阅channel,导致收到多变消息;

2.1 jedis.unsubscribe不好用,一直在报client是null的错,解决方案是,在登陆的时候发送特定消息,在onMessage中判定加密后的特定字符串,如果相等,则在JedisPubSub的onMessage中直接调用unsubscribe取消订阅,这样直接就解决了jedis.unsubscribe的问题

3.在thread中无法正常注入jedisPool,只能在thread的构造方法中传入,其实,对于我的功能来说这是一个不错的方法,因为还需要传入别的参数

下面直接上代码,有问题可以联系我。

 


/**
登陆
**/
public class LoginController {
    @Autowired
    private MsgLauncher msgLauncher;
    @PostMapping(value = "/login")
    public Odin  login(@RequestBody  User user){
        msgLauncher.prepared(u.getId());
        msgLauncher.lanuch(u.getId());
        return odin;
    }
}

 

 

/**
消息登陆器
*/
package com.zzj.msg;

import com.zzj.msg.whistleRedis.WhistleRedisThread;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisPool;

/**
 * Created by Administrator on 2019/1/7.
 */
@Service
public class MsgLauncher {

    @Autowired
    JedisPool jedisPool;
    @Autowired
    private RedisTemplate redisTemplate;

    public MsgLauncher() {}

    public  void lanuch (String userID) {
        WhistleRedisThread thread = new WhistleRedisThread(userID,jedisPool);
        thread.start();
    }
    public  void prepared (String userID) {
        WhistleRedisThread thread = new WhistleRedisThread(userID,jedisPool,true);
        thread.start();
    }

}

 

 

/**
阻塞处理
*/
package com.zzj.msg.whistleRedis;

import com.zzj.security.encrypter.MD5Cheater;
import com.zzj.utils.Constants;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;


@Slf4j
public class WhistleRedisThread extends Thread {

    private String userID;
    private JedisPool jedisPool;
    private boolean clean = false;

    @Override
    public void run() {
        Jedis jedis = jedisPool.getResource();
        try {

            if(clean){
                //取消订阅
                WhistleRedisHandler.Sender.send(jedis,userID,MD5Cheater.md5(Constants.CHANNEL_KEY));
                log.info("=================unsubscribe CHANNEL=============================");
                log.info("=================================================================");
                log.info("=================【" + userID + "】===============================");
                log.info("=================================================================");
                log.info("=================unsubscribe CHANNEL=============================");
                return;
            }
            WhistleRedisHandler.Getter getter =  new WhistleRedisHandler.Getter();
            jedis.subscribe(getter,userID);
            log.info("=================subscribe CHANNEL=============================");
            log.info("=================================================================");
            log.info("=================【" + userID + "】===============================");
            log.info("=================================================================");
            log.info("=================subscribe CHANNEL=============================");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            return;
        }
    }

    public WhistleRedisThread(String userID,JedisPool jedisPool) {
        this.userID = userID;
        this.jedisPool = jedisPool;
    }
    public WhistleRedisThread(String userID,JedisPool jedisPool,boolean clean) {
        this.userID = userID;
        this.jedisPool = jedisPool;
        this.clean = clean;
    }

    public WhistleRedisThread() {
    }
}

 

/**
真正的处理器
*/
package com.zzj.msg.whistleRedis;

import com.zzj.security.encrypter.MD5Cheater;
import com.zzj.utils.Constants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPubSub;

/**
 * Created by Administrator on 2019/1/7.
 */
@Slf4j
public class WhistleRedisHandler  {

    public WhistleRedisHandler() {
    }

    public static class Getter extends JedisPubSub{

        @Override
        public void onMessage(String channel, String message) {
            //订阅消息
            if(MD5Cheater.md5(Constants.CHANNEL_KEY).equals(message)){
                this.unsubscribe(channel);
                log.info("YesCommander,Cancel This Channel [" + channel +"]");
            }
            log.info("message is comming: channel is: " + channel );
            log.info("message is comming: message is: " + message );
        }

        @Override
        public void onSubscribe(String channel, int subscribedChannels) {
            // 订阅了频道 channel
            log.info("Subscribe success: channel is: " + channel );
            log.info("Subscribe success: subscribedChannels is: " + subscribedChannels );
        }

        @Override
        public void onUnsubscribe(String channel, int subscribedChannels) {
            log.info("Unsubscribe channel: channel is: " + channel );
            log.info("Unsubscribe channel: subscribedChannels is: " + subscribedChannels );
        }

    }
    public static class Sender {
        public static void send(Jedis jedis,String channel, String message){
            jedis.publish(channel,message);
        }
    }
}

 

 


/**
常量
**/
package com.zzj.utils;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Constants {

    public  static String CHANNEL_KEY;

    @Value(value = "${spring.redis.pubsub.keywords.unsubscribe}")
    public  void setChannelKey(String channelKey) {
        CHANNEL_KEY = channelKey;
    }
}

 

可以查看详细内容

 

你可能感兴趣的:(JAVA,redis,消息队列)