下单后半小时未付款订单自动取消的实现,延迟队列

下单后半小时未付款订单自动取消的实现,延迟队列

类似的需要:
订单的评论如果7天未评价,系统需要自动产生一条评论
订单的15天之后未点击收货,系统需要自动更改为已收货。
。。。

因为是需要一个常驻进程来检查的,我们使用redis存储会更好。

延迟队列
就是需要延迟一段时间后执行。Redis可通过zset来实现。我们可以将有序集合的value 设置为我们的消息任务(orderId),把value的score设置为消息的到期时间,然后轮询获取有序集合的中的到期消息进行处理。

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
	返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
	原始返回值:
		1) "jack"
		2) "2500"
		3) "tom"
		4) "5000"
		5) "peter"
		6) "12000"

	$redis->zrangebyscore返回值:
		array | null
		[value1 => score1, value2 => score2]

代码:



class RedisCli {
	protected static $redis;

	public static function getRedisInstance($conf){
		if(isset(self::$redis)){
			return self::$redis;
		}
		try{
			$redis = new \Redis();
			$re = $redis->connect($conf['host'], $conf['port']);
			if(!$re){
				throw new \Exception('connect redis error');
			}

			if(isset($conf['auth'])){
				$re = $redis->auth($conf['auth']);
				if(!$re){
					throw new \Exception('redis auth error');
				}
			}

			self::$redis = $redis;
			return $redis;
		}catch(\Exception $e){
			throw new \Exception($e->getMessage());
		}
	}

	public static function addOrder($redis, $key, $orderId){
		$ttl = time() - 600;
		$re = $redis->zadd($key, $ttl, $orderId);
		return $re;
	} 

	public static function getOrders($redis, $key, $num = 1){
		/*
			array(1) {
			  [3334]=>
			  float(1573703310)
			}
		*/
		$orders = $redis->zrangebyscore($key, 0, time(), ['limit'=>[0, $num], 'withscores'=>TRUE]);
		if(!$orders){
			return [];
		}
		// 移除元素
		foreach($orders as $k => $v){
			$redis->zrem($key, $k);
		}
		
		return $orders;
	}
	
	public static function getAndDeleteOrder($redis, $key, $num = 1){
		$script = " local score= redis.call(
					  'ZRANGEBYSCORE', KEYS[1], ARGV[1], ARGV[2], 'WITHSCORES' , 'LIMIT' , ARGV[3] , ARGV[4]
					)
					if score ~= false and #score ~= 0 then
					  local i = 1
					  while i <= #score do
					    redis.call('ZREM', KEYS[1] , score[i])
					    i=i+2
					  end
					end
					return score";

		$result = $redis->eval($script, [$key, 0, time(), 0, $num], 1);

		/*
			$redis->eval的返回结果:
			array(4) {
			  [0]=>
			  string(4) "3334"
			  [1]=>
			  string(10) "1573701647"
			  [2]=>
			  string(4) "3333"
			  [3]=>
			  string(10) "1573702072"
			}
		*/
		$return = [];
		$count = count($result);
		if($count > 0){
			for($i = 0; $i < $count; $i+=2){
				$return[$result[$i]] = $result[$i + 1];
			}
		}

		/*
			array(1) {
			  [3334]=>
			  string(10) "1573702856"
			}
		*/
		return $return;
	}
}

$conf = ['host'=>'127.0.0.1', 'port'=>6379, 'auth'=>'123456'];
$redis = RedisCli::getRedisInstance($conf);
$key = 'orderkey';
$orderId = 3334;

// 添加
RedisCli::addOrder($redis, $key, $orderId);

// 消费,由于可能存在多进程的并发,使用Lua优化版,
while(true){
	try{

		// $data = RedisCli::getOrders($redis, $key);
		$data = RedisCli::getAndDeleteOrder($redis, $key);

		if(empty($data)){
			sleep(1);
		}else{
			var_dump($data);
		}
		
	}catch(\Exception $e){

	}
}

你可能感兴趣的:(PHP,Redis)