设计场景

1、开启Redis的键空间过期事件(键过期发布任务),创建订单创建一个过期的key,按照订单号为key,设置过期时间。

2、通过Redis的订阅模式(持久阻塞),获取到订单号进行组装。

3、Redis通过订阅模式获取到已经过期的key,把该key加入think-queue 任务队列(依赖于Redis的队列,队列配置推荐使用Redis存储消息)。

4、开启一个队列的消费守护进程,进行消费。如果消费失败,则任务队里不会自动删除

遇到的问题

cli 模式报错

[2019-11-20T14:33:25+08:00][ error ] [8]PDO::prepare(): send of 68 bytes failed with errno=32 Broken pipe
error

相关代码

class RedisSubscribe
{
public function subscribe()
{
$redis = BaseRedis::plocal();
$redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
$redis->psubscribe(array('__keyevent@1__:expired'), function ($redis, $pattern, $chan, $msg) {
Log::info('[订阅事件] 过期KEY ' . $msg);
$flag = strstr($msg,":");
if(empty($flag)){
Log::error('[订阅事件] 非法的消息类型 '.$msg);
return false;
}
$originData = explode(':',$msg);
$event_key = $originData[1]??'0';
$event_status = $originData[0]??'0';
if($event_status == RedisTaskQueue::ORDER_TIMEOUT_EVENT){
Log::info('[订阅事件] 订单超时事件 '.$event_key);
$order = PayOrderModel::get(['order_no' => $event_key]);
Log::info('[订阅事件] 订单 '.$event_key);
if ($order['status'] == 0) {
$updateData = [
'id' => $order['id'],
'status' => 2,
'notify_status' => 0,
'pay_time' => time(),
'remark' => '超时队列事件'
];
$res = PayOrderModel::update($updateData);
Log::info('[订阅事件] 更新订单 ' . json_encode($res));
}
}
});
}
}

问题就出现在以上代码中查询和更新数据库问题

$order = PayOrderModel::get(['order_no' => $event_key]); 

问题分析,这里采用的Redis的订阅模式,持久性的

解决办法,通过任务队列解决

$isPushed = redis_queue(RedisTaskQueue::QUEUE_EVENT, $data);

把redis订阅服务获取到的信息,发布think-queue队列中,在队列中在进行处理数据库相关操作  

第二天出错

[2019-11-21T09:12:41+08:00][ error ] [队列事件] 队列执行失败 SQLSTATE[HY000]: General error: 2006 MySQL server has gone away|#0 /home/www/web/wiot.tinywan.com/thinkphp/library/think/db/Connection.php(844): think\db\Connection->query('SELECT * FROM `...', Array, false, false)
#1 /home/www/web/www.tinywan.com/thinkphp/library/think/db/Query.php(3132): think\db\Connection->find(Object(think\db\Query))
#2 /home/www/web/www.tinywan.com/thinkphp/library/think/db/Query.php(3193): think\db\Query->find(NULL)

数据库配置开启需要断线重连(第一次出错的时候正式环境没有开启该配置参数)

// 是否需要断线重连
'break_reconnect' => false,  

  

参考:

https://github.com/RunnerLee/fastd-eloquent/issues/5  

PHP系列 | PDO::prepare(): send of 68 bytes failed with errno=32 Broken pipe的更多相关文章

  1. fwrite(): send of 8192 bytes failed with errno=104 Connection reset by peer

    问题:fwrite(): send of 8192 bytes failed with errno=104 Connection reset by peer 问题描述 通过mysql + sphinx ...

  2. PHP PDO prepare()、execute()和bindParam()方法详解

    每次将查询发送给MySQL服务器时,都必须解析该查询的语法,确保结构正确并能够执行.这是这个过程中必要的步骤,但也确实带来了一些开销.做一次是必要的,但如果反复地执行相同的查询,批量插入多行并只改变列 ...

  3. PDO::prepare

    PDO::prepare — 准备要执行的SQL语句并返回一个 PDOStatement 对象(PHP 5 >= 5.1.0, PECL pdo >= 0.1.0) 说明 语法 publi ...

  4. Ubuntu 12.04 64bit 配置完android 5.0编译环境后出现“could not write bytes: Broken pipe.”而无法进入输入帐号密码的登陆界面

    Ubuntu 12.04 64bit 配置完android 5.0编译环境后出现“could not write bytes: Broken pipe.”而无法进入输入帐号密码的登陆界面.上网问了问百 ...

  5. MySQL测试环境遇到 mmap(xxx bytes) failed; errno 12解决方法

    查看Mysql日志 InnoDB: Initializing buffer pool, size = 128.0M InnoDB: mmap( bytes) failed; errno InnoDB: ...

  6. mysql报错mmap(137428992 bytes) failed; errno 12,Cannot allocate memory for the buffer pool

    mysql以`systemctl start mysqld.service`的方式启动一段时间后发现突然无法启动,尝试重新启动也不能解决问题,排查问题时,先后通过`systemctl status m ...

  7. Cannot send, channel has already failed:

    背景: 一个同事往这个队列发数据,另一个同事从这个队列取数据,进行解析. 这是昨天同事昨天消费者 消费activemq 队列,一开始有正常,运行了一段时间后,发现突然消费者变为零了.因为有监控.之后怎 ...

  8. org.apache.activemq.transport.InactivityIOException: Cannot send, channel has already failed

    项目是使用activeMQ 发布订阅的模式,在本地测试正常,但是 放到服务器上出现这个错误: org.apache.activemq.transport.InactivityIOException: ...

  9. Send [1] times, still failed

    com.alibaba.rocketmq.client.exception.MQClientException: Send [1] times, still failed, cost [696094] ...

随机推荐

  1. echart绘制GDP数据

    {% extends "base.html" %} {% block self_head_css_js %} {% endblock %} {% block main_conten ...

  2. 关于c3p0的重连机制(转载)

    1)C3P0容错和自动重连与以下配置参数有关: breakAfterAcquireFailure :true表示pool向数据库请求连接失败后标记整个pool为block并close,就算后端数据库恢 ...

  3. expdp、impdp导入导出数据库

    一.创建逻辑目录,该命令不会在操作系统创建真正的目录,最好以system等管理员创建.create directory dpdata1 as 'd:\test\dump'; 二.查看管理理员目录(同时 ...

  4. JVM 运行时数据区:程序计数器、Java 虚拟机栈和本地方法栈,方法区、堆以及直接内存

    Java 虚拟机可以看作一台抽象的计算机,如同真实的计算机,它也有自己的指令集和运行时内存区域. Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存(运行时内存区域)划分为若干个不同的数 ...

  5. C++学习(7)—— 函数提高

    1. 函数默认参数 在C++中,函数的形参列表中的形参是可以有默认值的 语法:返回值类型 函数名 (参数=默认值){} 注意 如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值 ...

  6. C/C++解题常用STL大礼包 含vector,map,set,queue(含优先队列) ,stack的常用用法

    每次忘记都去查,真难啊 /* C/C++解题常用STL大礼包 含vector,map,set,queue(含优先队列) ,stack的常用用法 */ /* vector常用用法 */ //头文件 #i ...

  7. C#程序 -- 以管理员权限运行

    阅读目录 一.判断程序是否以管理员身份运行 C#程序以管理员权限运行 在Vista 和 Windows 7 及更新版本的操作系统,增加了 UAC(用户账户控制) 的安全机制,如果 UAC 被打开,用户 ...

  8. accept返回的socket的端口号和连接socket一样的!!! socket绑定信息结构

    今天与同学争执一个话题:由于socket的accept函数在有客户端连接的时候产生了新的socket用于服务该客户端,那么,这个新的socket到底有没有占用一个新的端口? 讨论完后,才发现,自己虽然 ...

  9. 学习:简单使用MFC创建对话框窗口

    MFC介绍:微软基础类库(英语:Microsoft Foundation Classes,简称MFC)是微软公司提供的一个类库(class libraries),以C++类的形式封装了Windows ...

  10. F Energy stones

    题意是,有$n$个石头,每个石头有初始能量$E_i$,每秒能量增长$L_i$,以及能量上限$C_i$,有$m$个收能量的时间点,每次把区间$\left[S_i, T_i\right]$石头的能量都给收 ...