這次來是因爲上篇文章swoole實現多對多羣聊進階篇的pdo鏈接有效時間好像不是那麼的長!!!導致數據完全丟失!!!
通過測試,發現pdo鏈接在一個小時左右後就不會再往數據庫插入數據了,而且sql語句執行也不會報錯,具體什麼情況一時間還不清楚,然後開始找是不是pdo鏈接不支持長鏈接,百度了一下,果然需要配置才能實現長鏈接
PDO有一項參數,名爲PDO::ATTR_PERSISTENT是個booleans值,默認不開啓,
屁顛屁顛的開啓跑去測試後發現,還是不行,還是會斷開,這我又想不通了,如果斷開,執行sql理應會報錯啊,然後在try catch 語句下手
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute();
} catch (Exception $e) {
$this->pdo = new PDO(
"mysql:host=********;dbname=******;charset=utf8;",
"********",
"********",[\PDO::ATTR_CASE => \PDO::CASE_NATURAL]
);
}
這裏的意圖是,如果執行sql報錯,我就從新初始化pdo鏈接,不過結果也不盡人意,執行sql什麼事都沒有,數據庫沒有記錄,也不報錯,想來想去,只能是小弟搞得鬼了,要把小弟拉出去祭天!
上篇文章中的小弟相關的代碼
//創建token找幾個小弟
$this->ws->set([
'worker_num' => WORKER_NUM,
'task_worker_num' => TASK_WORKER_NUM,
]);
//token線程監聽
$this->ws->on("task", [$this, 'onTask']);
$this->ws->on('finish',[$this,'onFinish']);
我就想,會不會是小弟線程導致的問題,pdo鏈接被他吃掉了,後來一看代碼
/**
* 監聽用戶鏈接事件,鏈接時需要帶用戶id與房間id參數,再把用戶存到房間域中
*/
public function onOpen($server, $request){
//加入房間域
$this->redis->hset($request->get['room'], $request->get['uid'], $request->fd);
//加入組集合
$this->redis->sadd('group', $request->get['room']);
//鏈接後,插入一條日誌
self::recordLog($request->fd, $request->get['uid'], $request->get['room'], "add");
}
在用戶開始鏈接websocket的時候,我也會去數據庫插一條記錄,並沒有用到小弟,所以小弟的嫌疑就沒了…
這下就沒轍了,只好換跳思路實現功能了,跑到swoole官網,找到了異步MySQL客戶端相關的介紹,本着試一試的心態,把代碼貼進去
public $pdo = null;
public $dbdata = null;
public $sql = null;
public function __construct()
{
...//其他代碼
$this->pdo = new swoole_mysql();
$this->dbdata = array(
'host' => '******',
'port' => 3306,
'user' => '*********',
'password' => '*******',
'database' => '*********',
'charset' => 'utf8', //指定字符集
'timeout' => 2, // 可選:連接超時時間(非查詢超時時間),默認爲SW_MYSQL_CONNECT_TIMEOUT(1.0)
);
...//其他代碼
}
...//其他代碼
//房間記錄 fd-uid-room
private function recordLog($fd, $uid, $room_id, $type)
{
switch ($type){
case "add":
$this->sql = "***********";
break;
case "close":
$this->sql = "**********";
break;
}
try {
$this->pdo->connect($this->dbdata, function ($db, $r) {
if ($r === false) {
var_dump($db->connect_errno, $db->connect_error);
die;
}
$this->pdo->query($this->sql, function(swoole_mysql $db, $r) {
if ($r === false)
{
var_dump($db->error, $db->errno);
}
elseif ($r === true )
{
var_dump($db->affected_rows, $db->insert_id);
}
$db->close();
});
});
} catch (Exception $e) {
echo "recordLog error";
}
}
public function onTask($server, $task_id, $from_id, $data)
{
switch ($data['type']){
case "change":
self::push_room($data['room'], $data);
break;
}
return true;
}
/**
* 監聽關閉事件的回調
*/
public function onClose($ser, $fd)
{
//退出房間清除redis
$group = $this->redis->sMembers('group');
foreach ($group as $v) {
$fangjian = $this->redis->hgetall($v);
if(empty($fangjian)) continue;
foreach ($fangjian as $k => $vv) {
if ($fd == $vv) {
$this->redis->hdel($v, $k);
self::recordLog($vv, $k, $v, "close");
break 2;
}
}
}
}
具體代碼大家可以參考上一篇文章swoole實現多對多羣聊進階篇,代碼都是大同小異,這裏可以看到,我沒有讓小弟去處理onclose事件了,因爲測試中,線程不能調用swoole_mysql的事件,所以小弟還有有嫌疑的,這裏就不先追究了,代碼改完後運行,第一次鏈接,插入一條記錄,間隔兩小時後,第二次鏈接,發現可以實現功能效果!
完事收工!之前的代碼出錯的原因值得深入調查,但是目前還沒有查到,大家知道的可以評論告訴一下下我(認真臉)