专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
OSC开源社区  ·  Bun ... ·  昨天  
OSC开源社区  ·  RAG市场的2024:随需而变,从狂热到理性 ·  昨天  
码农翻身  ·  漫画 | 为什么大家都愿意进入外企? ·  2 天前  
程序员的那些事  ·  印度把 DeepSeek ... ·  3 天前  
51好读  ›  专栏  ›  SegmentFault思否

Redis 实现队列

SegmentFault思否  · 公众号  · 程序员  · 2017-11-30 08:00

正文

场景说明:

  • 用于处理比较耗时的请求,例如批量发送邮件,如果直接在网页触发执行发送,程序会出现超时

  • 高并发场景,当某个时刻请求瞬间增加时,可以把请求写入到队列,后台在去处理这些请求

  • 抢购场景,先入先出的模式

命令:

rpush + blpop 或 lpush + brpop

rpush : 往列表右侧推入数据
blpop : 客户端阻塞直到队列有值输出

简单队列:

simple.php

  1. $stmt = $pdo->prepare('select id, cid, name from zc_goods limit 200000');

  2. $stmt->execute();

  3. while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

  4.    $redis->rPush('goods:task', json_encode($row));

  5. }

  6. $redis->close();

获取20000万个商品,并把json化后的数据推入goods:task队列

queueBlpop.php

  1. // 出队

  2. while (true) {

  3.     // 阻塞设置超时时间为3秒

  4.    $task = $redis->blPop(array('goods:task'), 3);

  5.    if ($task) {

  6.        $redis->rPush('goods:success:task', $task[1]);

  7.        $task = json_decode($task[1], true);

  8.        echo $task['id'] . ':' . $task['cid'] . ':' . 'handle success';

  9.        echo PHP_EOL;

  10.    } else {

  11.        echo 'nothing' . PHP_EOL;

  12.        sleep(5);

  13.    }

  14. }

设置blpop阻塞时间为3秒,当有数据出队时保存到goods:success:task表示执行成功,当队列没有数据时,程序睡眠10秒重新检查goods:task是否有数据出队

cli 模式执行命令:
  1. php simple.php

  2. php queueBlpop.php

优先级队列

思路:

blpop 有多个键时,blpop会从左至右遍历键,一旦一个键能弹出元素,客户端立即返回。例如:

  1. blpop key1 key2 key3 key4

从key1到key4遍历,如果哪个key有值,则弹出这个值,若多个key同时有值时,优先弹出排在左边的key。

priority.php

  1. // 设置优先级队列

  2. $high = 'goods:high:task';

  3. $mid = 'goods:mid:task';

  4. $low = 'goods:low:task';

  5. $stmt = $pdo->prepare('select id, cid, name from zc_goods limit 200000');

  6. $stmt->execute();

  7. while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

  8.    // cid 小于100放在低级队列

  9.    if ($row['cid'] < 100) {

  10.        $redis->rPush($low, json_encode($row));

  11.     }

  12.    // cid 100到600之间放在中级队列

  13.    elseif ($row['cid'] > 100 && $row['cid'] < 600) {

  14.        $redis->rPush($mid, json_encode($row));

  15.    }

  16.    // cid 大于600放在高级队列

  17.    else {

  18.        $redis->rPush($high, json_encode($row));

  19.     }

  20. }

  21. $redis->close();

priorityBlop.php

  1. // 优先级队列

  2. $high = 'goods:high:task';

  3. $mid = 'goods:mid:task';

  4. $low = 'goods:low:task';

  5. // 出队

  6. while(true){

  7.    // 优先级高的队列放在左侧

  8.    $task = $redis->blPop(array($high, $mid, $low), 3);

  9.    if ($task) {

  10.        $task = json_decode($task[1], true);

  11.        echo $task['id'] . ':' . $task['cid'] . ':' . 'handle success';

  12.        echo PHP_EOL;

  13.    } else {

  14.        echo 'nothing' . PHP_EOL;

  15.        sleep(5);

  16.    }

  17. }

优先级高的队列放在blpop命令左侧,依次排序,blpop命令会依次弹出high, mid, low队列的值

cli 模式执行命令:
  1. php priority.php

  2. php priorityBlpop.php

延迟队列

思路:

可以用一个有序集合来保存延迟任务,member保存任务内容,score保存(当前时间 + 延时时间)。用时间作为score。程序只要用有序集合的第一条任务的score和当前时间做比较,如果当前时间比score小,说明有序集合的所有任务还没到执行时间。

delay.php

  1. $stmt = $pdo->prepare('select id, cid, name from zc_goods limit 200000');

  2. $stmt->execute();

  3. while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

  4.    $redis->zAdd('goods:delay:task', time() + rand(1, 300), json_encode($row));

  5. }

将20万条任务导入有序集合goods:delay:task,所有任务延迟到之后的1秒到300秒内执行

delayHandle.php

  1. while (true) {

  2.    // 因为是有序集合,只要判断第一条记录的延时时间,例如第一条未到执行时间

  3.    // 相对说明集合的其他任务未到执行时间

  4.    $rs = $redis->zRange('goods:delay:task', 0, 0, true);

  5.    // 集合没有任务,睡眠时间设置为5秒

  6.    if (empty($rs)) {

  7.        echo 'no tasks , sleep 5 seconds' . PHP_EOL;

  8.        sleep (5);

  9.        continue;

  10.    }

  11.    $taskJson = key($rs);

  12.    $delay = $rs[$taskJson];

  13.    $task = json_decode($taskJson, true);

  14.    $now = time();

  15.    // 到时间执行延时任务

  16.    if ($delay <= $now) {

  17.        // 对当前任务加锁,避免移动移动延时任务到任务队列时被其他客户端修改

  18.        if (!($identifier = acquireLock($task['id']))) {

  19.            continue;

  20.        }

  21.        







请到「今天看啥」查看全文