为应对办公时良(la)好(ji)的网络连接,防止数据库操作在连接中超时丢失,本人封装了断网重连的函数,如果需要的小伙伴欢迎使用~
本PHP函数基于TP5进行开发,如有其他需求,也可根据逻辑自行封装处理
如对断网重连要求不高,可参考《ThinkPHP5.x完全开发手册》中“连接数据库”章节的参数break_reconnect’ => true
断网重连方法相关参数api文档
breaklineReconnection($queryCb, $config = [])
$queryCb参数类型 | 应用参考 | 备注 |
---|---|---|
string | ‘select * from tableName’ | 直接使用字符串,方法内部封装了db类进行处理 |
callable | function($dbObj){return $dbObj->name(‘tableName’)->count();} | 回调函数返回数据集,使用内置的$dbObj防止断网后dB静态的旧连接失效 |
$config参数 | 必填 | 类型 | 参考值 | 默认值 | 备注 |
---|---|---|---|---|---|
simpleReturn | 否 | int | 1 | 0 | 1为简单格式,0为详细格式。返回的数据格式为简单格式,不返回具体错误信息,直接获取sql会返回的数据集 |
dbConnect | 否 | string/array | ‘db_local’ | [] | DB所要连接的数据库参数配置,默认为空,跟dB::connnect()所要填充的第一项内容一致,可参考tp5相关写法 |
forceDbConnFresh | 否 | boolean | false | false | 是否强制重新连接DB数据库。默认为false,在断网重连后,必定会处理为true进行重连。非必要是不调整为true |
ignoreNotify | 否 | boolean | false | true | 是否echo重连数据库信息相关提示 |
<?php
namespace expand;
use think\Exception;
use think\exception\PDOException;
/*
* @title 断网重连继续sql操作
* @author millionmile<[email protected]>
*/
class BreaklineReconnection
{
static $sleepTime = 5; //每次睡眠暂停的时间
static $dbObj = []; //存放最新的dB连接
static $intervalNum = 1; //每隔一段时间重新执行
private static $currentDbConnect = [];
private static $defualtConfig = [
'simpleReturn' => 0,
'dbConnect' => [],
'forceDbConnFresh' => false,
'ignoreNotify' => true,
'customOperate' => false //
];
/**
* 初始化默认的断线重连参数
* @param array $config
*/
public static function setConfig($config = [])
{
self::$defualtConfig['simpleReturn'] = $config['simpleReturn'] ?? self::$defualtConfig['simpleReturn'];
self::$defualtConfig['dbConnect'] = $config['dbConnect'] ?? self::$defualtConfig['dbConnect'];
self::$defualtConfig['forceDbConnFresh'] = $config['forceDbConnFresh'] ?? self::$defualtConfig['forceDbConnFresh']; //强制重连刷新
self::$defualtConfig['ignoreNotify'] = $config['ignoreNotify'] ?? self::$defualtConfig['ignoreNotify']; //是否忽略重连通知
self::$defualtConfig['customOperate'] = $config['customOperate'] ?? self::$defualtConfig['customOperate']; //是否使用自定义的sql查询操作
}
/**
* @title 切换连接的数据库,保证该连接是可用的
* @param null $dbObj
*/
public static function getDbObj($dbConnect = [], $forceFresh = false)
{
return self::reconnCb(
function () use ($dbConnect, $forceFresh) {
//切换为新的数据库连接
return db('', $dbConnect, $forceFresh);
},
function () use ($dbConnect) {
return self::getDbObj($dbConnect, true);
}, [
'resetIntervalNumFlag' => false //避免影响到定时间隔
]
);
}
/**
* @title 服务器断开的话的断线重连
* @param $queryCb callable|string 回调函数形式的话,最终也要返回字符串的sql
* @param array $config 配置项,可不填写
* simpleReturn 返回的数据格式为简单格式,不返回具体错误信息,直接获取sql会返回的数据集
* dbConnect 连接数据库的配置项,可以用来选择要连接的库
* forceDbConnFresh 强制重连,默认一开始进入不需要重连,无需人工配置
* ignoreNotify 是否查看连接数据库信息
* @return array|bool|mixed
*/
public static function breaklineReconnection($queryCb, $config = [])
{
//如果直接返回值,simpleReturn为1
$simpleReturn = $config['simpleReturn'] ?? self::$defualtConfig['simpleReturn'];
$dbConnect = $config['dbConnect'] ?? self::$defualtConfig['dbConnect'];
$forceDbConnFresh = $config['forceDbConnFresh'] ?? self::$defualtConfig['forceDbConnFresh']; //强制重连刷新
$ignoreNotify = $config['ignoreNotify'] ?? self::$defualtConfig['ignoreNotify']; //是否忽略重连通知
if (is_array($dbConnect)) {
$dbConnectStr = md5(json_encode($dbConnect));
} else {
$dbConnectStr = $dbConnect;
}
//如果不是当前的,需要重新初始化db
//重新获取dbObj
if (self::$dbObj[$dbConnectStr] === null || $forceDbConnFresh) {
if ((!$ignoreNotify) && $forceDbConnFresh) {
echo '重连数据库' . PHP_EOL;
}
self::$dbObj[$dbConnectStr] = self::getDbObj($dbConnect, $forceDbConnFresh);
}
return self::reconnCb(
function () use ($queryCb, &$config, $simpleReturn, $forceDbConnFresh, $ignoreNotify, $dbConnectStr) {
//如果不是使用自定义操作,则传sql
switch (true) {
//如果是回调函数,就是使用直接执行方法
case is_callable($queryCb) === true:
$data = $queryCb(self::$dbObj[$dbConnectStr]); //如有特殊需要,如切换数据库,建议使用内置getDbObj方法获取
break;
//如果是sql语句
case is_string($queryCb) === true:
$data = self::$dbObj[$dbConnectStr]->query($queryCb);
break;
default:
if ($simpleReturn) {
return false;
} else {
return [
'code' => 0,
'msg' => '暂不支持的sql执行方式,请使用字符串或回调函数',
'data' => []
];
}
break;
}
unset($config['forceDbConnFresh']); //取消强制重连
if ($simpleReturn) {
return $data;
} else {
return [
'code' => 0,
'msg' => '执行成功,返回结果',
'data' => $data
];
}
},
function () use ($queryCb, &$config) {
$config['forceDbConnFresh'] = true; //重连连接数据库
return self::breaklineReconnection($queryCb, $config);
},
[
'simpleReturn' => $simpleReturn,
'dbConnect' => $dbConnect,
'forceDbConnFresh' => $forceDbConnFresh,
'ignoreNotify' => $ignoreNotify,
]
);
}
/**
* @title 重连回调函数执行
* @param callable $execCb
* @param callable $errCb
* @param array $config
* @return mixed
*/
private static function reconnCb(callable $execCb, callable $errCb, array $config = [])
{
$ignoreNotify = $config['ignoreNotify'] ?? self::$defualtConfig['ignoreNotify'];
$resetIntervalNumFlag = $config['resetIntervalNumFlag'] ?? true; //重置间隔数字
$errSelfCb = function () use ($errCb, $ignoreNotify) {
$currentSleepTime = intval(self::$sleepTime * self::$intervalNum);
//每次重连的间隔延长
self::$intervalNum++;
if (!$ignoreNotify) {
echo '连接数据库失败,' . $currentSleepTime . '秒后重新执行' . PHP_EOL;
}
sleep($currentSleepTime);
return $errCb();
};
try {
$res = $execCb();
//上面执行成功,重新调整失败重连间隔时间
if ($resetIntervalNumFlag) {
self::$intervalNum = 1;
}
return $res;
} catch (\Exception $e) {
//如果是mysql连接失败,重新执行方法
$errMsg = $e->getMessage();
if (strpos($errMsg, 'SQLSTATE[HY000]') !== false && (strpos($errMsg, '2002') !== false or strpos($errMsg, '2006') !== false)) {
return $errSelfCb();
}
// echo $errMsg;
// exit;
}
}
}
技术有限,如有更好的方式方法处理,还请不吝指教!