redis消息队列的四种实现方式之List的简单队列和延时队列

目录

redis为我们提供的数据结构是很多的,如果再Redis中要实现一个消息中间件至少有4种方式:

  1. List
  2. zSet
  3. PUB/SUB(发布与订阅)
  4. Steam

基于List实现

最简单是基于List来实现,如果说你的业务需求足够的简单,同时又想享有消息中间件的一部分功能,有没有实力来部署一个消息中间件的东西,那么就可以使用List。

List提供了从头或者尾去放入元素,也提供了从头或者尾去拿取元素,而且操作时间都非常的快,那么我们就可以把List视为一个队列,我从左边往里放元素,再从右边拿元素,很完美的符合消息队列的一个简单的模型。
Redis-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只是从左边拿数据,就不多解释了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容