redis队列

常用的队列有RabbitMQ,Redis,ZeroMQ, ActiveMQ, Kafka等,其中redis是较为轻量级的,这里简单介绍下。

Redis这个缓存的应用之所以能用作队列,要得益于lpushbrpop两条命令:

    lpush 在list左侧插入数据
    rpush 在list右侧插入数据
    brpop 在list右侧弹出数据(如果没有数据,就阻塞进程)
    blpop 在list左侧弹出数据(如果没有数据,就阻塞进程)

要实现队列,就让生产者不断将任务lpush到队列中,另一边消费者不断brpop取出任务去处理。

因为brpopblpop是阻塞性的,所以不用自己写sleep指令了。

#消费者伪码如下
while(true) {
    task = BRPOP myquene 0
    //返回值是一个数组,第一个是"myquene"队列名,第二个是弹出的数据
    execute $task  //执行任务
}

BRPOP命令接收两个参数,第一个是键值,第一个是超时时间(单位秒)。当超过了时间,就返回nil(空)。如果超时时间设置为0,表示永不超时,永远等待(直到取到了元素)。

    #向redis中push一条数据
    127.0.0.1:6379> lpush myquene "c"
    (integer) 1

    #从redis中pop一条数据
    127.0.0.1:6379> brpop myquene 0
    1) "myquene"
    2) "c"

java示例代码如下:

    @Test void testQuene() {
        //push
        jedis.lpush("myquene", "a");
        jedis.lpush("myquene", "b");
        jedis.rpush("myquene", "c");
        jedis.rpush("myquene", "d");

        //pop
           List<String> stringList = jedis.blpop(0, "myquene");
        for(String index : stringList) {
            System.out.println("----");
            System.out.println(index);
        }

        stringList = jedis.brpop(0, "myquene");
        for(String index : stringList) {
            System.out.println("++++++");
            System.out.println(index);
        }
        for(int i=1; i<=30; i++) {
            System.out.println(i);
            System.out.println(jedis.brpop(0, "myquene"));
        }
    }

Redis队列的优先级

  1. redis提供指令lpushrpush,来了高优先级的队列把它push到队列的最前端。 这个方法的缺点是高优先的任务之间的执行顺序是先进后出。
  2. 上面提供的方法,仅仅做到了高优先级的比低优先级的先执行;但是高优先级的任务之间的顺序。解决办法是:设置两个队列,一个高优先级队列,一个低优先级队列。高优先级任务放到高优队列,低优先级任务放低优队列。
    消费者读取的时候先从高优队列读取,如果没有任务,再从低优先级队列读取。public void test() {
    Jedis jedis = new Jedis(“ip”, 6379);
    while(true) {
    List data = jedis.brpop(0, “high”, “low”);
    execute(data[1]);
    }
    }
    上面的代码会阻塞地从”high”和”low”队列中取任务,如果”high”队列中没有再从”low”队列中取。
  3. 优先级级别很多的情况
    假设有个这样的需求,优先级不是简单的高中低或者0-10这些固定的级别。而是类似0-99999这么多级别。那么我们第三种方案将不太合适了。
    虽然redis有sorted set这样的可以排序的数据类型,看是很可惜它没有阻塞版的接口。于是我们还是只能使用list类型通过其他方式来完成目的。
    有个简单的做法我们可以只设置一个队列,并保证它是按照优先级排序号的。然后通过二分查找法查找一个任务合适的位置,并通过 lset 命令插入到相应的位置。
    例如队列里面包含着写优先级的任务[1, 3, 6, 8, 9, 14],当有个优先级为7的任务过来,我们通过自己的二分算法一个个从队列里面取数据出来反和目标数据比对,计算出相应的位置然后插入到指定地点即可。
    因为二分查找是比较快的,并且redis本身也都在内存中,理论上速度是可以保证的。但是如果说数据量确实很大的话我们也可以通过一些方式来调优。
  4. 回想我们第三种方案,把第三种方案结合起来就会很大程度上减少开销。例如数据量十万的队列,它们的优先级也是随机0-十万的区间。我们可以设置 10个或者100个不同的队列,0-一万的优先级任务投放到1号队列,一万-二万的任务投放到2号队列。这样将一个队列按不同等级拆分后它单个队列的数据 就减少许多,这样二分查找匹配的效率也会高一点。但是数据所占的资源基本是不变的,十万数据该占多少内存还是多少。只是系统里面多了一些队列而已。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

(Spamcheck Enabled)