之前研究了一下两个MQ,这俩挺靠谱的。倒是有个B说他们用的是redis的消息的订阅来做的队列,我挺好奇的于是乎在网上抄了一份,这个东西挺快的但是可能会丢数据可靠性比消息队列差,从体量上来说感觉就也很好理解。

这个功能貌似主要基于redis的List功能,这个结构可以模拟队列(先进先出)和栈(先进后出)

这个例子一开始是单纯的使用lpush插入和rpop取出的的方法实现的,但是这个方式在创建时没啥问题,没有这个list就创建一个,但是如果rpop把所有元素去光之后就把缓存删除了,直接就报错了。所以就要做一个阻塞的东西。(很迷我也不太懂,大概是没东西了他就等着吧)

然后以下是抄来的代码

首先是pom.xml 要有这两个

<dependency>
		    <groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
		    <groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-redis</artifactId>
		    <version>1.3.2.RELEASE</version>
		</dependency>

在srcource里面新建一个redis.properties内容如下

redis.url=localhost
redis.port=6379
redis.maxIdle=30
redis.minIdle=10
redis.maxTotal=100
redis.maxWait=10000

连接相关的配置

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

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

public class JedisPoolUtils {

    private static JedisPool pool = null;

    static {

        //加载配置文件
        InputStream in = JedisPoolUtils.class.getClassLoader().getResourceAsStream("redis.properties");
        Properties pro = new Properties();
        try {
            pro.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //获得池子对象
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(Integer.parseInt(pro.get("redis.maxIdle").toString()));//最大闲置个数
        poolConfig.setMaxWaitMillis(Integer.parseInt(pro.get("redis.maxWait").toString()));//最大闲置个数
        poolConfig.setMinIdle(Integer.parseInt(pro.get("redis.minIdle").toString()));//最小闲置个数
        poolConfig.setMaxTotal(Integer.parseInt(pro.get("redis.maxTotal").toString()));//最大连接数
        pool = new JedisPool(poolConfig, pro.getProperty("redis.url"), Integer.parseInt(pro.get("redis.port").toString()));
    }

    //获得jedis资源的方法
    public static Jedis getJedis() {
        return pool.getResource();
    }

    public static void main(String[] args) {
        Jedis jedis = getJedis();
        System.out.println(jedis);
    }
}

然后使用线程模拟并发 首先是生产者

import com.example.demo.config.JedisPoolUtils;

import redis.clients.jedis.Jedis;

public class MessageProducer extends Thread {
    public static final String CHANNEL_KEY = "channel:1";
    private volatile int count;

    public void putMessage(String message) {
        Jedis jedis = JedisPoolUtils.getJedis();
        Long publish = jedis.publish(CHANNEL_KEY, message);//返回订阅者数量
        //System.out.println(Thread.currentThread().getName() + " put message,count=" + count+",subscriberNum="+publish);
        count++;
    }

    @Override
    public synchronized void run() {
        for (int i = 0; i < 5; i++) {
            putMessage("message" + count);
        }
        //putMessage("quit");
    }

    public static void main(String[] args) {
        MessageProducer messageProducer = new MessageProducer();
        Thread t1 = new Thread(messageProducer, "thread1");
        Thread t2 = new Thread(messageProducer, "thread2");
        Thread t3 = new Thread(messageProducer, "thread3");
        Thread t4 = new Thread(messageProducer, "thread4");
        Thread t5 = new Thread(messageProducer, "thread5");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}

然后是消费者

import com.example.demo.config.JedisPoolUtils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
public class MessageConsumer implements Runnable {
    public static final String CHANNEL_KEY = "channel*";//频道

    public static final String EXIT_COMMAND = "exit";//结束程序的消息

    private MyJedisPubSub myJedisPubSub = new MyJedisPubSub();//处理接收消息

    public void consumerMessage() {
        Jedis jedis = JedisPoolUtils.getJedis();
        jedis.psubscribe(myJedisPubSub, CHANNEL_KEY);//第一个参数是处理接收消息,第二个参数是订阅的消息频道
    }

    @Override
    public void run() {
        while (true) {
            consumerMessage();
        }
    }

    public static void main(String[] args) {
        MessageConsumer messageConsumer = new MessageConsumer();
        Thread t1 = new Thread(messageConsumer, "thread5");
        Thread t2 = new Thread(messageConsumer, "thread6");
        t1.start();
        t2.start();
    }
}

/**
 * 继承JedisPubSub,重写接收消息的方法
 */
class MyJedisPubSub extends JedisPubSub {
    @Override
    /** JedisPubSub类是一个没有抽象方法的抽象类,里面方法都是一些空实现
     * 所以可以选择需要的方法覆盖,这儿使用的是SUBSCRIBE指令,所以覆盖了onMessage
     * 如果使用PSUBSCRIBE指令,则覆盖onPMessage方法
     * 当然也可以选择BinaryJedisPubSub,同样是抽象类,但方法参数为byte[]
     **/
    public void onPMessage(String pattern, String channel, String message) {
        System.out.println(Thread.currentThread().getName()+"-接收到消息:pattern="+pattern+",channel=" + channel + ",message=" + message);        //接收到exit消息后退出
        if (MessageConsumer.EXIT_COMMAND.equals(message)) {
            System.exit(0);
        }
    }
}

然后就是退订unsubscribe据说只能根据这个方法来退订

import com.example.demo.config.JedisPoolUtils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;

public class Test111 {
    public static void main(String[] args) {
        Jedis jedis = JedisPoolUtils.getJedis();
        System.out.println("订阅前");
        jedis.subscribe(new JedisPubSub() {
            @Override
            public void onMessage(String channel, String message) {
                if("quit".equals(message)){
                    unsubscribe("channel:1");
                }
                System.out.println(message);
            }

            @Override
            public void unsubscribe(String... channels) {
                super.unsubscribe(channels);
            }
        }, "channel:1");
        System.out.println("订阅后");
    }
}

反正是实现了,具体的事情我还要再深入研究