php多进程单例模式下的 MySQL及Redis连接错误修复
2015-10-19 09:19
471 查看
问题描述:
前几天写了个php常驻脚本,主要逻辑如下//跑完数据后休息60秒 $sleepTime = 60; $maxWorker = 10; while (true) { $htmlModel = new DetailHtmlModel(); //新抓取的html数目 $count = $htmlModel->getCount(array( array('status', '=', DetailHtmlModel::STATUS_UNDEAL) )); if ($count > 0) { //将抓取的html数据放入队列 Queue::putHtmlData(); } $queueLength = Queue::getHtmlQueueLength(); if ($queueLength > 0) { //启动的worker数目,限制启动的个数 $workerCount = min($maxWorker, ceil($queueLength / 10)); runWorker($workerCount); $sleepTime = 60; } else { //无数据的话每次多停5秒 $sleepTime += 5; } unset($htmlModel); sleep($sleepTime); }
看代码知道,我根据待处理的数据量启动最多10个的worker去处理数据,每个worker是一个进程,但跑起来后遇到两个错误:
一个是mysql的:MySQL server has gone away
另一个是redis的:Uncaught exception ‘RedisException’ with message ‘read error on connection’
原因:
出现MySQL server has gone away 常见的原因就是连接时间超过了mysql设置的wait_timeout值,这时mysql会主动关掉连接,默认是8小时。但是我是启动脚本就出现这个错误,而且使用连接前都先做了ping,不可能是连接超时了,又看了mysql官网对这个错误可能原因的说明(原文)里面有这么一句:You can also encounter this error with applications that fork child processes, all of which try to use the same connection to the MySQL server. This can be avoided by using a separate connection for each child process.
很多公司代码在创建mysql和redis的连接实例时,采用的都是单例模式,我们也一样。也就是说无论外部new了多少实例,只要句柄存在就会被返回,而不是重新创建连接实例。刚好我又启动了多个进程去处理数据,然后每个进程又使用同一个连接实例,就导致了报错
解决办法:
通常只有在cli运行模式下才有可能出现超时的情况,所以在构建mysql单例句柄的key时使用getmypid()加入进程id,这样就能把每个进程使用的连接实例分隔开
mysql:
private static function _getHandleKey($params) { //解决多进程会保持同一个连接的错误 if (PHP_SAPI === 'cli') { $params['pid'] = getmypid(); } ksort($params); return md5(implode('_' , $params)); }
redis也使用同样方法解决问题
相关文章推荐
- CentOS安装Redis记录
- phpredis:php一个key-value扩展
- redis cluster
- hiredis之坑爹的异步调用
- Redis GEO 测试
- Redis GEO 特性简介
- Spring Cache集成redis
- Redis 集群教程
- redis_常见问题
- redis_常用命令
- PHP-redis编译成功
- Redis入门(一)
- 1. Redis集群研究和实践(基于redis 3.0.5)
- Julia : 如何进一步改进操作Redis的效率?
- [redis]Redis Transaction
- PHP-Redis扩展使用手册(一)
- 高并发读写压力如何处理?redis内存数据库使用场景引发的思考
- Redis Scan命令
- Redis OBJECT命令
- linux安装redis服务以及php redis扩展