Medoo+swoole组合

前言

本篇重点是Medoo的断线重连和主从读写,没啥swoole的内容

Medoo

  1. medoo是什么 : 我觉得就是个封装好了PHP使用PDO操作数据库的一个框架;
  2. github地址
  3. medoo使用文档

swoole断线重连描述

-----------------------------------------下面是一张图片-------------------------------------------------
Medoo+swoole组合_第1张图片
-----------------------------------------图片结束-------------------------------------------------

描述:

用swoole 的相信都遇到过这个问题,我的解决方案是找到Medoo代码中所有sql执行的最终位置,
然后再那里捕获到 2006 的异常, 然后进行重连
最终发现是下面这个函数

public function exec($query, $map = [])
	{
		try{		
			if ($this->debug_mode)
			{
				echo $this->generate($query, $map);

				$this->debug_mode = false;

				return false;
			}

			if ($this->logging)
			{
				$this->logs[] = [$query, $map];
			}
			else
			{
				$this->logs = [[$query, $map]];
			}

			@$statement = $this->pdo->prepare($query);

			if ($statement)
			{
				foreach ($map as $key => $value)
				{
					$statement->bindValue($key, $value[ 0 ], $value[ 1 ]);
				}

				$statement->execute();
				$this->statement = $statement;
				return $statement;
			}
		} catch(\PDOException $e){
			// 这段代码是我写得
			if(in_array($e->errorInfo[1], [2006,2013])){ // 重新连接
				$useDbConfig = require(ROOT_PATH . '/config/common.php');
				if(strpos($query, 'SELECT') === 0){ // 主从区分, 目前这个区分个人感觉不是很好,如果有好建议的大神, 还请赐教,我先谢谢了
					// getDbConfig() 这个函数实现在下面
					self::$_readInstance = self::getInstance(getDbConfig($useDbConfig, 'slave'),'slave', $this); // 只读
					return self::$_readInstance->exec($query, $map);
				}else{
					self::$_instance = self::getInstance(getDbConfig($useDbConfig, 'default'),'default', $this); // 写
					return self::$_instance->exec($query, $map);
				}
			}
		}
		return false;
	}

PDO 连接的属性和option如下(getDbConfig()函数声明)

function getDbConfig($useDbConfig, $key)
{
    $defaultDb = $useDbConfig['mysql'][$key];
    $dbConf = [
        'database_type' => 'mysql',
        'database_name' => $defaultDb['db'],
        'server'        => $defaultDb['host'],
        'username'      => $defaultDb['user'],
        'password'      => $defaultDb['pwd'],
        'port'          => $defaultDb['port'],
        'charset'       => 'utf8',
        'option'        => [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,//需要将错误处理模式,变为异常模式, 这个可以让其抛出2006的错误
            PDO::ATTR_STRINGIFY_FETCHES  => false,// 提取的时候将数值转换为字符串。
            PDO::ATTR_EMULATE_PREPARES => false, // 启用或禁用预处理语句的模拟。 
            PDO::ATTR_PERSISTENT => true, // 持久化链接
            PDO::MYSQL_ATTR_USE_BUFFERED_QUERY  => true, // 使用缓冲查询 

        ]
    ];

    return $dbConf;
}

Medoo的单例和websocket中使用遇到的, 单例失效问题

个人比较喜欢贴代码说话如下:

// 记得要将构造函数私有化, 下面是单例实现, 这段代码贴到Medoo里面即可
	private static $_instance; // 写对象
	private $_instanceVersion; // 写对象的版本号
	private static $_readInstance; // 只读对象
	private $_readInstanceVersion; // 只读对象版本号
/**
	* 获取Medoo的单例
	* @param $nowConnct : 调用的当前Medoo对象, 防止当前进程的mysql连接可用的时候,又重新PDO连接; 利用对象版本号作为区分
	*/
	public static function getInstance($dbConf, $key, $nowConnct=NULL)
	{
		if($key == 'default'){
			if(!is_null($nowConnct)){ // 这里只有 断线重连才会触发(errorCode:2006)
				// nowVersion == newVersion : 当前的和最新的版本号一致,那么重新连接然后更新版本号;
				if($nowConnct->_instanceVersion == self::$_instance->_instanceVersion){ 
					self::$_instance = NULL; // 这个设置空,就会自动重新连接
				}
			}
			if(is_null(self::$_instance)) {
				self::$_instance = new self($dbConf);
				// getMillisecond() 我自己定义的一个获取毫秒时间戳的函数,你们可以用你们自己设计的
				self::$_instance->_instanceVersion = getMillisecond(); // 更新版本号
				__accessLog('Version : ' . self::$_instance->_instanceVersion);
			}
			return self::$_instance;
		}
		elseif ($key == 'slave') {
			if(!is_null($nowConnct)){
				if($nowConnct->_readInstanceVersion == self::$_readInstance->_readInstanceVersion){ 
					self::$_readInstance = NULL;
				}
			}
			if(is_null(self::$_readInstance)) {
				self::$_readInstance = new self($dbConf);
				self::$_readInstance->_readInstanceVersion = getMillisecond();

			}
			return self::$_readInstance;
		}
	}

这里讲一下对象版本号的作用, 就是这两个属性:
_instanceVersion;_readInstanceVersion (其实是一样的作用)
websocket 中我发现下面的现象:

多次连接的异常是:
系统连续发出三个查询: a b c
a 发现断了, 然后它就重连在查询, 然后完毕
b 也发现断了, 然后它也重连在查询, 然后完毕
c 也发现断了, 然后它也重连在查询, 然后完毕
问题是: 这里不应该b,c 也重连, 因为a 拿到了最新的连接, 应该把这个连接直接给b,c即可;因为单例了啊;

解决方案:
需要保证 a 重连的时候, b,c的连接也能拿到最新的
然后我设计了对象版本号, 原理如下:
a重连的时候会将单例里面的对象变成最新的, b,c执行sql的时候会发现2006了,此时他们肯定走异常重连处理;如果我能把a产生的对象给他们就ok了

如果我给每次的对象都加上版本号使用的时候就可以:

a,b,c 同时执行查询的时候他们的对象都是A对象本号是100: 简写 A(100)
执行查询的时候 a 查询发现2006, 然后走了重连
重连之前我加了版本好判断: a 查询用的是 A(100), 单例里面是 A(100)
对比发现是同一个对象, 这时候 就产生一个新的 对象 A(101) 返回给 a 查询去使用

这时候 b 查询开始了:

b 查询带着 A(100) 他发现2006了, 走重连;
重连之前判断下版本号, 发现: b 查询用的是 A(100) , 单例里面已经是 A(101)
对比, 发现 版本号不一致; 就表示, 有新的 对象可用, 那么 b 查询就不重连连接了,直接拿着 a 查询产生的 A(101) 使用就可以了

结语:

具体的代码实现可以看上面的单例实现就可以了
第一次设计, 如果有更好的方案,或者有些别的框架已经实现的思路也请各位看官告知我, 谢谢大佬先;
小弟邮箱: [email protected]

你可能感兴趣的:(php,swoole,medoo)