今天程序要实现一个功能 就是 一个答疑系统 学员提出问题 然后老师回答问题 如果两个小时后 老师没有回答问题 就提醒助教来回答问题
然后问题就出在这里 怎么能实现 如果学员提出问题 老师没有回答 怎么在两个小时后 提醒助教
最开始的想法 就是 定时任务 扫表
用mysql 查回答状态为没回答的
写个php脚本 while true ,没查到就sleep 2 查到就处理 每次查询加limit 一次10条
或者使用 crontab 一分钟一次也行
然后我们决定使用redis 的key失效的时候 来 触发一个回调事件
下面是我做这个功能的记录
第一步 是修改redis的配置文件
Key过期事件的Redis配置
这里需要修改redis的配置的文件,配置 notify-keyspace-events 的参数为 “Ex”。x 代表了过期事件。notify-keyspace-events “Ex” 保存配置后,重启Redis服务,使配置生效。
先开启一个redis-cli客服端,订阅所有操作,等待接收消息
Psubscribe keyevent@0:expired
2.再开启一个终端,redis-cli 进入 redis,新增一个 10秒过期的键
10秒过期后,接收到的消息如下:
如果这个时候 消息 接收到了 就证明 配置没有问题了
接下来 就是写代码
第一步
设置过期事件:index.php
1. require_once './Redis.class.php';
2. $redis = new \Redis();
3. $order_id = 123;
4. $redis->setex('order_id',10,$order_id);
过期事件的订阅:psubscribe.php
1. require_once './Redis.class.php';
2. $redis = new \Redis();
3. // 解决Redis客户端订阅时候超时情况
4. $redis->setOption();
5. $redis->psubscribe(array('__keyevent@0__:expired'), 'keyCallback');
6. // 回调函数,这里写处理逻辑
7. function keyCallback($redis, $pattern, $chan, $msg)
8. {
9. echo "Pattern: $pattern\n";
10. echo "Channel: $chan\n";
11. echo "Payload: $msg\n\n";
12. }
先用命令行模式执行 psubscribe.php
在浏览器访问
http://localhost/index.php
(这里是我的路径,具体可以改成你自己的路径)
命令行窗口就会有如下信息输出:
这就是一个正常的流程
接下来是我遇到的一些问题
- 另外,每次改动命令行相关代码,都需要重新加载supervisor。
当我执行订阅消息 psubscribe.php 窗口开着的时候 进程是在的 窗口关了 进程就不在了
这个时候 就要 加在一个命令的最前面,表示不挂断的运行命令
- 后台执行
一般运行linux上的程序都是执行 .sh 文件(./sh文件),那如果不影响当前CMD窗口的操作,需要后台运行怎么办呢?
这时就需要借助 nohup 和 & 命令来实现。
(1) nohup
加在一个命令的最前面,表示不挂断的运行命令
(2) &
加载一个命令的最后面,表示这个命令放在后台执行
还有一个问题
解决phpredis ‘RedisException’ with message ‘read error on connection’
最近一个后台常驻job通过redis的brpop阻塞读取消息时,设置的超时时间较长
list(
k
e
y
,
key,
key,row)=
r
e
d
i
s
−
>
b
r
P
o
p
(
redis->brPop(
redis−>brPop(queue_name,3600); //超时时间为1小时
但是在实际的使用中发现很短时间后就会退出,通过查看error log,发现:‘RedisException’ with message ‘read error on connection’ 提示
经过一番折腾,原来发现是php.ini文件中的一个配置项导致:
default_socket_timeout = 60
由于redis扩展也是基于php 的socket方式实现,因此该参数值同样会起作用。
找到了问题就比较好解决了:
1、直接修改php.ini,将其设置为我们想要的值(这个不推荐)
2、在我们的脚本中通过以下方式设置,这样就比较灵活,不对其他脚本产生影响
ini_set('default_socket_timeout', -1); //不超时
public function psubscribe(){
ini_set('default_socket_timeout', -1);
$redis=new \Redis();
$redis->connect('127.0.0.1',6379);
$redis->setOption();
$redis->psubscribe(array('__keyevent@0__:expired'), function($redis, $pattern, $chan, $msg){
//这里可以写要执行的操作
echo 123;
}
这里是 执行监听订阅消息
这里就能看到 这个监听了
这个时候设置一个key有到期时间 时间到了 就会触发这个方法 触发里面的回调方法
回调方法里面的 $msg 就是到期的key 获取到key 就可以操作了
备注:
使用了一段时间因为我的回调函数里面有查数据库 发现启动后是好使的 过了一段时间就查数据库为空 上网查是说 mysql连接默认是8小时 过了8小时 就连接不上了
网上说
其中的mysql_ping()用来判断连接是否已经被断开了,若是断开了,关闭当前的链接,重新创建新的连接.
这样,只要发现连接被断开了,即可重新连接了.
function reconnect(){
if (!mysql_ping ($this->db)) {
//here is the major trick, you have to close the connection (even though its not currently working) for it to recreate properly.
mysql_close($this->db);
$this->connect();
}
}
thinkphp5的话
tp5 旧版本的bug (5.0.16 以下的版本存在) 使用新版本可以解决。
database.php 配置文件中
加入断线重连,可以解决
//断线重连
'break_reconnect' => true,
我现在是直接在回调函数里面写的pdo连接数据库,还在测试