目录
- 《redis消息队列的四种实现方式之List的简单队列和延时队列》(本篇)
- 《redis消息队列的四种实现方式之Sorted Set的延时队列》
redis为我们提供的数据结构是很多的,如果再Redis中要实现一个消息中间件至少有4种方式:
- List
- zSet
- PUB/SUB(发布与订阅)
- Steam
基于List实现
最简单是基于List来实现,如果说你的业务需求足够的简单,同时又想享有消息中间件的一部分功能,有没有实力来部署一个消息中间件的东西,那么就可以使用List。
生产者从左边去放元素,消费者就从右边拿。但是消费者什么时候拿有时候是不确定的,当不确定什么时候拿的时候,就得不停的去队列中看有没有该拿的元素。这种情况会发生CPU空转,浪费资源。
代码详解:
import com.zhao.utils.UuidUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 生产者
*/
@RestController
@RequestMapping("/testString")
public class ProducerController {
@Autowired
private RedisTemplate redisTemplate;
@PostMapping(value = "testMethodList")
public void producerList() {
// 创建String类型实例
ListOperations listOperations = redisTemplate.opsForList();
// 从左边添加数据
listOperations.leftPush("testListOne", UuidUtil.getUuid());
System.out.println("添加数据:" + listOperations.range("testListOne", 0, -1));
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 消费者
*/
@RestController
@RequestMapping(value = "/consumerController")
public class ConsumerController {
@Autowired
private RedisTemplate redisTemplate;
@PostMapping(value = "consumerList")
public void consumerList() {
// 创建List类型实例
ListOperations listOperations = redisTemplate.opsForList();
// 查询已有的数据
System.out.println(listOperations.range("testListOne", 0, -1));
while (true) {
System.out.println("正在拿数据");
// 从右边拿数据
Object testListOne = listOperations.rightPop("testListOne");
// 如果没有拿到数据 则重新拿 直到拿到数据再停止
if (!ObjectUtils.isEmpty(testListOne)) {
System.out.println("拿到数据:" + testListOne.toString());
break;
}
}
}
}
import com.zhao.RedistestApplication;
import com.zhao.controller.consumer.ConsumerController;
import com.zhao.controller.producer.ProducerController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(classes = RedistestApplication.class)
public class TestController {
@Autowired
private ProducerController producerController;
@Autowired
private ConsumerController consumerController;
/**
* 生产者
*/
@Test
public void testProducerList() {
producerController.producerList();
}
/**
* 消费者
*/
@Test
public void testConsumerList() {
consumerController.consumerList();
}
}
我们先启动消费者,当生产者没有往队列中放数据的时候,队列中现在还是空的,什么都没有。此时消费者就会一直不停地去访问队列,直到拿到数据为止。
这样就形成了“数据库空转”。当启动了生产者,生产者往队列中放入一条数据,消费者拿到数据,循环停止。
Brpop
命令移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 基本语法:
redis 127.0.0.1:6379> BLPOP LIST1 LIST2 .. LISTN TIMEOUT
如果列表为空,返回一个 nil 。 否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值。
java 中的实例:
/**
* 2
* 延时拿数据
*/
@PostMapping(value = "consumerList")
public void consumerList() {
// 创建Jedis实例
Jedis jedis = new Jedis();
System.out.println("开始拿数据了 testListOne现有数据:" + jedis.lrange("testListOne", 0, -1));
// 从右边阻塞式拿数据 超时时间为0代表不限时
List<String> testListOne = jedis.brpop(0, "testListOne");
System.out.println("拿到数据:" + testListOne);
}
在以上实例中,操作会被阻塞,如果“testListOne”列表有数据,就会从列表的右边拿取第一个数据并返回,否则会无限期等待(可能是无限期,我自己就等了1分钟);如果超时时间大于0,会阻塞设置的时间;如果超时时间小于0,启动项目会报错。
同理,Blpop
只是从左边拿数据,就不多解释了。
网友评论