背景
之前有一个调用学校接口的个人PHP项目,因为学校接口做了限制,不可以同时请求,一旦同时请求学校那边就会判定为非法。
这种情况下用PHP+Redis做队列是最理想的选择,PHP对学校接口发出一次请求后,进入Redis队列,等待队列中排在前面的请求结束后再处理本次请求。这样就不会再出现同时请求的问题了。
代码
// 创建请求ID标志, uniqid 无法保证唯一, 自己去搜索生成唯一的方法
$uuid = uniqid();
$tsk_name = "mytask";
$time_out = 30; // 超时策略: 30秒
$time_start = time();
$redis->rPush($tsk_name, $uuid); // 右(后)插入队列
// 堵塞等待队列中第一个和$uuid匹配的(到我了)
while($uuid != $redis->lGet($tsk_name, 0)){
if((time()-$time_start)> $time_out) {
break; // 超时跳出(某些原因队列异常了, 可能永远取不到)
}
usleep(10); // sleep 10ms, 再次尝试
}
// 这里执行任务的处理代码....
// $response 已拼装好要返回的内容
// 处理完成后(数据库等已入库更新), 需要:
if($redis->lGet($tsk_name, 0) == $uuid){ // 再次确认第一个是本请求
$redis->lPop($tsk_name); // 完成任务了, 从队列中移除
}else{
// 出现这种情况, 是因为超时了, 或前面的$uuid没有被消费
// 若不清除, 后续的请求, 都将无法正常进入队列执行
// 取队列中的所有$uuid
$queues = $redis->lRange($tsk_name, 0, -1);
foreach($queues as $i=>$uid){
if($uid==$uuid){
// 队列中仍存在当前的$uuid
// 清除$uuid以前异常的队列, 让后边的请求得以正常排队
$redis->lTrim($tsk_name, $i+1, -1);
break;
}
}
}
// 响应内容
return $response;
使用Redis队列时要注意把握好超时和「出队」的判定,不然就容易出问题了。
只用redis做过订阅发布模型和Cache,队列我都是微软的MQ,和Rabbit,最近换了腾讯的CMQ,腾讯这个还是不错的。有空可以了解一下~
学习了
PHP 操作 Redis 就是简单哈