这里有新鲜出炉的 PHP 面向对象编程,程序狗速度看过来!
PHP(外文名: Hypertext Preprocessor,中文名:"超文本预处理器")是一种通用开源脚本语言。语法吸收了 C 语言、Java 和 Perl 的特点,入门门槛较低,易于学习,使用广泛,主要适用于 web 开发领域。PHP 的文件后缀名为 php。
下面小编就为大家带来一篇 PHP 实现电商订单自动确认收货 redis 队列。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
一、场景
之前做的电商平台,用户在收到货之后,大部分都不会主动的点击确认收货,导致给商家结款的时候,商家各种投诉,于是就根据需求,要做一个订单在发货之后的 x 天自动确认收货。所谓的订单自动确认收货,就是在在特定的时间,执行一条 update 语句,改变订单的状态。
二、思路
最笨重的做法,通过 linux 后台定时任务,查询符合条件的订单,然后 update。最理想情况下,如果每分钟都有需要 update 的订单,这种方式也还行。奈何平台太小,以及卖家发货时间大部分也是密集的,不会分散在 24 小时的每分钟。那么,定时任务的话,查询过多,不适合。这里可以先把将要自动确认收货的订单信息存储到其他介质上,比如 redis,memcache,rabbitmq,然后执行的脚本从前面的介质获取到订单信息来判断,这里可以大大的减少数据库的查询压力。
redis 队列的生产者
对此,我们选择每天在凌晨两点的时候,通过 linux 的定时任务把即将要确认收货的订单信息查询出来,然后存储在 redis 上,redis 上我们选择的队列,队列处理的特点就是先进先出,前面的数据在查询订单时,通过发货时间排序,所以最先出队列的肯定是距离规定的自动收货时间最近的订单。代码如下
- $successCount = 0;
- $failCount = 0;
- $screen_time = 3600 * 24 * 9; //设置筛选天数
- $data = array();
- $now_time = time();
- //查询符合要求的数据
- $sql = "select id,send_time as deliver_time from `order` where is_send=1 and is_del=0 and is_cancel=0 and is_token=0 and send_time>0 and send_time + {$screen_time} < $now_time
- order by send_time asc";
- $res = $con - >query($sql);
- //当队列还有数据时将数据记录并清除
- while ($redis - >LLEN('auto_recevice_order')) {
- $txt = '执行时间:'.date('Y-m-d H:i:s').',信息:'.$redis - >RPOP('auto_recevice_order');
- file_put_contents('./autoToken/fail_log.txt', $txt."\r\n".PHP_EOL, FILE_APPEND);
- $failCount++;
- }
- //重新填充数据进队列
- while ($row = $res - >fetch_assoc()) {
- $successCount++;
- $redis - >LPUSH('auto_recevice_order', json_encode($row1));
- }
- $con - >close();
- $success = date('Y-m-d H:i:s').':[推送成功]:本次成功推送数据:'.$successCount.'条;记录上次处理失败数据:'.$failCount."条\r\n";
- file_put_contents('./success_log.txt', $success."\r\n".PHP_EOL, FILE_APPEND);
redis 队列的消费者
队列的消费者没有通过 linux 的定时任务去做,用 linux 的 screen+php cli 模式执行 php 脚本,消费者只需要不断的从队列中读取订单信息,然后判断订单信息中的发货时间,如果达到自动收货的要求,就执行 update 语句。同时如果没有达到收货的时间,而且与收货时间间距比较大的时候,可以让 php 脚本休眠 sleep 一定的时间数,这个时间数自己调节设计,获取出来的未达到时间要求的订单,需要重新推送到 redis 队列中去,而且还是队列的顶端。以便下次获取。代码如下:
- $set_time = 3600 * 24 * 10; //设置几天后自动收货
- while (true) {
- if ($i % 30 == 0) {
- usleep(10); //防止while 循环使CPU使用率过高
- }
- if ($redis - >LLEN('auto_recevice_order')) {
- $data = json_decode($redis - >RPOP('auto_recevice_order'));
- $id = (int) $data - >id; //将数据转化为整形
- $deliver_time = (int) $data - >deliver_time; //将数据转化为整形
- $res1 = $res2 = false;
- $now_time = time();
- if (($deliver_time + $set_time) < $now_time) {
- $sql1 = "update `order` set `is_token`='1',`token_time` = $now_time where id=$id and is_send=1 and is_del=0 and is_cancel=0 and is_token=0 and send_time + {$set_time} < $now_time";
- $res1 = $con - >query($sql1); //更新数据
- $rows = mysqli_affected_rows($con);
- if ($rows) {
- $ip = $this - >getIp();
- $sql2 = "insert into `order_log`(`order_id`,`log_msg`,`log_ip`,`log_role`,`log_user`,`log_order_state`,`log_time`) VALUES($id,'系统自动收货','$ip','系统','服务器','收货',$now_time)"; //写入订单日志
- $res2 = $con - >query($sql2); //添加日志数据
- }
- }
- if ($res1 == false) { //将没达到条件的数据重新插入队列中
- $redis - >RPUSH('auto_recevice_order', json_encode(array('id' = >$id, 'deliver_time' = >$deliver_time)));
- }
- }
- $i++;
- }
这里执行 php 脚本,需要用到 linux 的 screen 或者 supervisor、nohup 守护进程。具体用法可自行百度. 同样脚本里面最好有必须的日志记录。
三、思考
随着业务的增长,在队列中同一秒内,存在的多个需要处理的订单,而一次只能从队列中取出一个相关订单信息的时候,可以采用一个生产者多个消费者的模式,这种情况下,可以用到锁机制,保证一条消息只能到达一个消费者。当 redis 数据达到一定的量之后,也可以适当的调整生产者的执行频率和对应的条件。
以上这篇 PHP 实现电商订单自动确认收货 redis 队列就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持 PHPERZ。
来源: http://www.phperz.com/article/17/0810/339841.html