【redis】使用sorted set写延时队列,进行多次重试操作

背景
公司帖子审核,需要把内容发送给第三方平台进行,之前只做了一次提交,成功就成功失败就失败了;但有时候因为网络问题失败的情况还是挺多的,于是要做一个重试的操作

Redis 有序集合(sorted set)

Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

我们把有序集合的分数,记录为下次要执行某段逻辑的时间戳,当到这个时间之后,获取分数为当前时间之前的数据,再进行开始没有提交成功的提交操作,这样就达到了延时的效果。

代码展示

/** 假装前边有一万行代码 ,然后走到提交后,发现失败了**/
$redis = redisConnect(); // 假装redis连上了
$RETRY_TIME = 5; // 假如5分钟重试1次
$SUBMIT_FAIL = "submit_fail"; // 假装key名称就叫这个
$time = 60*($RETRY_TIME);
$timeStamp = time();
// 存请求失败的数据
$zData = [
	'dataId' => $dataId, // 来自前边1w行代码
    'appId' => $appId,   // 来自前边1w行代码
    'reviewContext' => $reviewContext, // 来自前边1w行代码
    'title' => $title, // 来自前边1w行代码
];
$redis->ZADD($SUBMIT_FAIL,$timeStamp+$time,json_encode($zData)); // 5分钟后,脚本拿到这条数据进行重试
$redis->DEL($SUBMIT_FAIL."_".$dataId); // 删除可能存在的脏数据
$redis->INCR($SUBMIT_FAIL."_".$dataId); // 记录帖子提交重试的次数

上面的操作完成了失败后记录下次执行的时间和数据,接下来就是要写一个脚本,来消费这个数据

public function mediaFailRetry(){
	 ini_set('default_socket_timeout', -1);
	 // 启动一个进程,随机执行5-10分钟,防止长时间进程锁死。
	 $timestamp=time();
	 $quit_time=rand(120,360)*50;
	 $redis = redisConnect(); // 还是假装连上redis
	
	 while(true){
	     if(time()-$timestamp > $quit_time){
	         return true;
	     }
	     // 获取分数在0~当前时间戳之间的数据
	     $tasks = $redis->ZRANGEBYSCORE(self::YIDUN_MEDIA_SUBMIT_FAIL,0,time());
	     if ($tasks) {
	     	 // 拿到符合条件的数据
	         foreach ($tasks as $task){
	             $taskArr = json_decode($task,true);
	             $dataId = $taskArr['dataId'];
	             $reviewContext = $taskArr['reviewContext'];
	             $title = $taskArr['title'];
	             $appId = $taskArr['appId'];
	             // 再次执行提交操作(省略1w行处理逻辑)
	             $flag = false; // 再次失败了
	             if(!$flag){
	                 // 如果重试没达到10次,延迟5分钟再重试
	                 if($redis->GET($SUBMIT_FAIL."_".$dataId) < $RETRY_COUNT){
	                     echo date('Y-m-d H:i:s') . ",dataId:({$dataId})重试提交不成功" . PHP_EOL;
	                     $time = 60*($RETRY_TIME);
	                     $timeStamp = time();
	                     // 存请求失败的数据
	                     $zData = [
	                         'appId' => $appId,
	                         'reviewContext' => $reviewContext,
	                         'dataId' => $dataId,
	                         'title' => $title,
	                     ];
	                     $redis->ZADD($SUBMIT_FAIL,$timeStamp+$time,json_encode($zData));
	                     $redis->INCR($SUBMIT_FAIL."_".$dataId);
	                 }else{
	                     echo date('Y-m-d H:i:s') . ",dataId:({$dataId})已重试".self::RETRY_COUNT."次提交不成功,结束重试" . PHP_EOL;
	                     $redis->DEL($SUBMIT_FAIL."_".$dataId);
	                 }
	                 continue;
	             }
	             // 能走到这里,说明成功了,其他逻辑,结束后续重试就可以了
	             $redis->DEL($SUBMIT_FAIL."_".$dataId);
	             echo date('Y-m-d H:i:s') . ",dataId:({$dataId})已重试提交成功,结束重试" . PHP_EOL;
	         }
	     }
	     sleep(60);
	 }
 }

redis实现延时队列比较适合轻量级的延时操作,重一点可以采用mq进行实现。
下一篇文章我会用rabbitmq来实现一下同样的操作

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值