之前研究了一下两个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("订阅后");
}
}
反正是实现了,具体的事情我还要再深入研究