PHP+Redis 实例【一】点赞 + 热度 下篇
这篇主要讲如何将数据保存回Mysql,但是里面还会涉及到如何将错误信息以及提示信息保存到文件里,方便以后的运维,再有就是如何使用PHP写进程BAT。
Redis数据刷回数据库前的知识准备
首先针对上篇提到的关于redis刷回数据库的安全性的设计模式,因为我们使用的是list来做数据索引,所以在我们将list数据提取出来的时候,一旦redis在这时候出现异常,就会导致刚提取出来的数据丢失!有些小伙伴就说,丢失就丢失呗,才一点数据。但是我们做程序,就应该以严谨为基础,所以下面就来说下Redis List这位大佬给我们提供了什么帮助。
Redis List -》RpopLpush()函数 使用方法:RPOPLPUSH source destination 说明:命令RPOPLPUSH在一个原子时间内,执行以下两个动作:①命令RPOPLPUSH在一个原子时间内,执行以下两个动作;②将source弹出的元素插入到列表destination,作为destination列表的的头元素。 设计模式:Redis的列表经常被用作队列(queue),用于在不同程序之间有序地交换消息(message)。一个程序(称之为生产者,producer)通过LPUSH命令将消息放入队列中,而另一个程序(称之为消费者,consumer)通过RPOP命令取出队列中等待时间最长的消息。
不幸的是,在这个过程中,一个消费者可能在获得一个消息之后崩溃,而未执行完成的消息也因此丢失。
使用RPOPLPUSH命令可以解决这个问题,因为它在返回一个消息之余,还将该消息添加到另一个列表当中,另外的这个列表可以用作消息的备份表:假如一切正常,当消费者完成该消息的处理之后,可以用LREM命令将该消息从备份表删除。
Redis数据刷回数据库
方面文字太多?没关系。下面先来一段代码!我们的主体部分:
index.php:
1 <?php 2 require_once(__DIR__."/Mysql.class.php"); 3 require_once(__DIR__."/Redis.class.php"); 4 require_once(__DIR__."/Output_Log.class.php"); 5 6 7 $rel = true; //无限循环的变量 8 $num = 0; //用来没数据时的判断依据 9 date_default_timezone_set("Asia/Shanghai"); 10 $now = date("Y-m-d H:i:s"); //当前时间 11 //file log 12 $txt = dirname(__DIR__)."/Script_Log/clickgood_log.txt"; 13 $output = new OutputLog(); 14 $test = $output->open($txt,"a+"); 15 16 while($rel) 17 { 18 $redis = new RedisCtrl(); 19 20 //开始干活 21 if($num==0){ 22 //这里就是将信息输出到文件里记录,下面很多地方都是一样的。 23 $text = "start ".$name."\n"; 24 echo $text; 25 $output->write($test,$text); 26 } 27 28 //获取备份队列的长度 29 $copylistlength = $redis->llen("comment:uploadcopylist"); 30 31 //我这里展示的是第一数据回滚到mysql,小伙伴想批量回滚的,自己改装下就可以用了。 32 //自己动手丰衣足食! 33 if($copylistlength>1) 34 { 35 //由于是单一数据回滚,所以我要判断它是否超过我设定的值,小伙伴们最好也自己定一个阈值。 36 //report error 37 echo $now." ->false\n"; 38 $rel = false; 39 return; 40 } 41 else if($copylistlength==1) 42 { 43 //这里判断防止上次redis出现错误,导致数据没有及时回到mysql 44 $data = $redis->rpop("comment:uploadcopylist"); 45 $rel = $redis->UpdateClickGoodDataToMysql($data); 46 $text = $rel."\n"; 47 echo $text; 48 $output->write($test,$text); 49 } 50 else 51 { 52 //获取主要队列的长度 53 $listlength = $redis->llen("comment:uploadlist"); 54 if ($listlength>0) { 55 //使用之前说到的设计模式 56 $data = $redis->rpoplpush("comment:uploadlist","comment:uploadcopylist"); 57 58 $rel = $redis->UpdateClickGoodDataToMysql($data); 59 $text = $rel."\n"; 60 echo $text; 61 $output->write($test,$text); 62 }else{ 63 // 队列为空 64 // 打印关闭信息,这里的写法算是维持进程窗口不关闭,需要手动关闭 65 // 如果想让它执行完自动关闭的, 66 // 把下面改写成$rel = false; 67 if($num<=3){ 68 $text = $now." -> please close .\n"; 69 echo $text; 70 $output->write($test,$text); 71 $num++; 72 } 73 else 74 { 75 $output->close($test); 76 } 77 } 78 } 79 80 }
Redis.class.php: redis操作类
1 <?php 2 class RedisCtrl 3 { 4 //init redis 5 static $redisIp = "127.0.0.1"; 6 static $redisPort =6379; 7 static $redisPass =""; 8 public $redis = null; 9 10 //Redis 11 public function __construct() 12 { 13 $this->redis = new Redis(); 14 $this->redis->connect(self::$redisIp,self::$redisPort); 15 $this->redis->auth(self::$redisPass); 16 } 17 18 public function llen($key) 19 { 20 $rel = $this->redis->llen($key); 21 return $rel; 22 } 23 24 public function rpop($key) 25 { 26 $rel = $this->redis->rpop($key); 27 return $rel; 28 } 29 30 public function rpoplpush($source,$destination) 31 { 32 $rel = $this->redis->rpoplpush($source,$destination); 33 return $rel; 34 } 35 36 public function UpdateClickGoodDataToMysql($data) 37 { 38 //get id and time from redis list 39 $result = json_decode($data,true); 40 $id = $result['id']; 41 $time = $result['time']; 42 $arr = array(); 43 44 //like 45 $like = $this->redis->zscore("comment:like",$id); 46 $like = $like?$like:0; 47 //hate 48 $hate = $this->redis->zscore("comment:hate",$id); 49 $hate = $hate?$hate:0; 50 51 $sql = "update comment_info set like_count=".$like.", hate_count=".$hate." where id=".$id; 52 $arr[] = $sql; 53 //update sql 54 $mysql = new MysqlCtrl(); 55 $mysql->saveMySQL($arr); 56 57 //更新完,将set集合里需要更新的id去掉 58 $this->redis->srem("comment:uploadset",$id); 59 //更新完毕,将备份队列里的数据去掉 60 $this->redis->lrem("comment:uploadcopylist",$data); 61 62 return $sql."\n"; 63 } 64 }
Mysql.class.php mysql类
1 <?php 2 //封装函数 3 class MysqlCtrl 4 { 5 //初始化参数 6 //数据库参数配置 7 static $dbms = "mysql"; 8 static $host = Your host; 9 static $user = Your user; 10 static $pass = Your pass; 11 static $database = Your database; 12 //睡眠时间 13 static $sleep = 1; 14 15 public $dsn = null; 16 public $dbh = null; 17 18 public function __construct() 19 { 20 $this->dsn = self::$dbms.":host=".self::$host.";dbname=".self::$database; 21 //return $dsn; 22 try { 23 $this->dbh = new PDO($this->dsn, self::$user, self::$pass); 24 echo "Connected\n"; 25 } catch (Exception $e) { 26 echo $this->dsn; 27 die("Unable to connect: " . $e->getMessage()); 28 } 29 } 30 31 //保存数据到数据库 32 //PDO 33 public function saveMySQL($arr) 34 { 35 36 try { 37 $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 38 39 $this->dbh->beginTransaction(); 40 41 $count = count($arr); 42 for ($i=0; $i < $count; $i++) { 43 $this->dbh->exec($arr[$i]); 44 } 45 46 $this->dbh->commit(); 47 48 } catch (Exception $e) { 49 $this->dbh->rollBack(); 50 echo "Failed: " . $e->getMessage()."\n"; 51 $json = json_encode($arr); 52 echo "False-SQL: ".$json."\n"; 53 exit(); 54 } 55 } 56 }
Output_Log.class.php 输出信息到文件的类
1 <?php 2 class OutputLog 3 { 4 public function open($name,$r) 5 { 6 $text = fopen($name, $r); 7 return $text; 8 } 9 10 public function write($name,$title) 11 { 12 $rel = fwrite($name, $title); 13 return $rel; 14 } 15 16 public function close($name) 17 { 18 fclose($name); 19 } 20 }
clickgood_log.txt 这里是保存输出的信息,里面是空白的。
hellO world
上面这些就是整套数据保存到mysql的操作,这是本人源码copy过来的,所以细分程度比较高,但是可扩展性也很高。有什么错误的地方希望小伙伴们能提出,谢谢。
最后就是我们的进程调用了,其实很简单
创建一个txt文件,然后改名为clickgood.bat,记得把txt后缀文件名改为bat
clickgood.bat:
1 D:\Software\wamp64\bin\php\php7.0.10\php.exe index.php
关于上面的bat注意两点,一前面那个php.exe,按照你自己的路径去找,我使用wampserver测试环境,基本就是上面的路径。第二点,后面的index.php和clickgood.bat同一目录下。
好了!终于可以收尾了!、
欢迎大家交流,上面只是本人自己的做法,希望大家能够指出错误的地方!或者提供更好的做法。