This commit is contained in:
as2252258@163.com
2021-09-14 01:27:50 +08:00
parent fde7fd4994
commit a934bded3c
+242 -231
View File
@@ -18,282 +18,293 @@ use Swoole\Timer;
class PDO implements StopHeartbeatCheck class PDO implements StopHeartbeatCheck
{ {
const DB_ERROR_MESSAGE = 'The system is busy, please try again later.'; const DB_ERROR_MESSAGE = 'The system is busy, please try again later.';
private ?\PDO $pdo = null; private ?\PDO $pdo = null;
private int $_transaction = 0; private int $_transaction = 0;
/** /**
* @var EventProvider * @var EventProvider
*/ */
private EventProvider $eventProvider; private EventProvider $eventProvider;
private int $_timer = -1; private int $_timer = -1;
private int $_last = 0; private int $_last = 0;
/** /**
* @param string $dbname * @param string $dbname
* @param string $cds * @param string $cds
* @param string $username * @param string $username
* @param string $password * @param string $password
* @param string $chatset * @param string $chatset
* @throws * @throws
*/ */
public function __construct(public string $dbname, public string $cds, public function __construct(public string $dbname, public string $cds,
public string $username, public string $password, public string $chatset = 'utf8mb4') public string $username, public string $password, public string $chatset = 'utf8mb4')
{ {
$this->eventProvider = Kiri::getDi()->get(EventProvider::class); $this->eventProvider = Kiri::getDi()->get(EventProvider::class);
} }
public function init() public function init()
{ {
$this->heartbeat_check(); $this->heartbeat_check();
$this->eventProvider->on(OnWorkerExit::class, [$this, 'stopHeartbeatCheck']); $this->eventProvider->on(OnWorkerExit::class, [$this, 'stopHeartbeatCheck']);
} }
/** /**
* @return bool * @return bool
*/ */
public function inTransaction(): bool public function inTransaction(): bool
{ {
return $this->_transaction > 0; return $this->_transaction > 0;
} }
/** /**
* *
*/ */
public function heartbeat_check(): void public function heartbeat_check(): void
{ {
if (env('state', 'start') == 'exit') { if (env('state', 'start') == 'exit') {
return; return;
} }
if ($this->_timer === -1 && Context::inCoroutine()) { if ($this->_timer === -1 && Context::inCoroutine()) {
$this->_timer = Timer::tick(1000, function () { $this->_timer = Timer::tick(1000, function () {
try { try {
if (env('state', 'start') == 'exit') { if (env('state', 'start') == 'exit') {
Kiri::getDi()->get(Logger::class)->critical('timer end'); Kiri::getDi()->get(Logger::class)->critical('timer end');
$this->stopHeartbeatCheck(); $this->stopHeartbeatCheck();
} }
if (time() - $this->_last > 10 * 60) { if (time() - $this->_last > 10 * 60) {
$this->stopHeartbeatCheck(); $this->stopHeartbeatCheck();
$this->pdo = null; $this->pdo = null;
} }
} catch (\Throwable $throwable) { } catch (\Throwable $throwable) {
error($throwable); error($throwable);
} }
}); });
} }
} }
/** /**
* *
*/ */
public function stopHeartbeatCheck(): void public function stopHeartbeatCheck(): void
{ {
if ($this->_timer > -1) { if ($this->_timer > -1) {
Timer::clear($this->_timer); Timer::clear($this->_timer);
} }
$this->_timer = -1; $this->_timer = -1;
} }
/** /**
* *
*/ */
public function beginTransaction() public function beginTransaction()
{ {
if ($this->_transaction == 0) { if ($this->_transaction == 0) {
$this->_pdo()->beginTransaction(); $this->_pdo()->beginTransaction();
} }
$this->_transaction++; $this->_transaction++;
} }
/** /**
* *
*/ */
public function commit() public function commit()
{ {
if ($this->_transaction == 0) { if ($this->_transaction == 0) {
$this->_pdo()->commit(); $this->_pdo()->commit();
} }
$this->_transaction--; $this->_transaction--;
} }
/** /**
* *
*/ */
public function rollback() public function rollback()
{ {
if ($this->_transaction == 0) { if ($this->_transaction == 0) {
$this->_pdo()->rollBack(); $this->_pdo()->rollBack();
} }
$this->_transaction--; $this->_transaction--;
} }
/** /**
* @param string $sql * @param string $sql
* @param array $params * @param array $params
* @return array * @return array
* @throws Exception * @throws Exception
*/ */
public function fetchAll(string $sql, array $params = []): bool|null|array public function fetchAll(string $sql, array $params = []): bool|null|array
{ {
$pdo = $this->queryPrev($sql, $params); $pdo = $this->queryPrev($sql, $params);
$result = $pdo->fetchAll(\PDO::FETCH_ASSOC); $result = $pdo->fetchAll(\PDO::FETCH_ASSOC);
$pdo->closeCursor(); $pdo->closeCursor();
return $result; return $result;
} }
/** /**
* @param string $sql * @param string $sql
* @param array $params * @param array $params
* @return array * @return array
* @throws Exception * @throws Exception
*/ */
public function fetch(string $sql, array $params = []): bool|null|array public function fetch(string $sql, array $params = []): bool|null|array
{ {
$pdo = $this->queryPrev($sql, $params); $pdo = $this->queryPrev($sql, $params);
$result = $pdo->fetch(\PDO::FETCH_ASSOC); $result = $pdo->fetch(\PDO::FETCH_ASSOC);
$pdo->closeCursor(); $pdo->closeCursor();
return $result; return $result;
} }
/** /**
* @param string $sql * @param string $sql
* @param array $params * @param array $params
* @return array * @return bool|array|null
* @throws Exception * @throws \Exception
*/ */
public function fetchColumn(string $sql, array $params = []): bool|null|array public function fetchColumn(string $sql, array $params = []): bool|null|array
{ {
$pdo = $this->queryPrev($sql, $params); $pdo = $this->queryPrev($sql, $params);
$result = $pdo->fetchColumn(\PDO::FETCH_ASSOC); $result = $pdo->fetchColumn(\PDO::FETCH_ASSOC);
$pdo->closeCursor(); $pdo->closeCursor();
return $result; return $result;
} }
/** /**
* @param string $sql * @param string $sql
* @param array $params * @param array $params
* @return int * @return int
* @throws Exception * @throws Exception
*/ */
public function count(string $sql, array $params = []): int public function count(string $sql, array $params = []): int
{ {
$pdo = $this->queryPrev($sql, $params); $pdo = $this->queryPrev($sql, $params);
$result = $pdo->rowCount(); $result = $pdo->rowCount();
$pdo->closeCursor(); $pdo->closeCursor();
return $result; return $result;
} }
/** /**
* @param string $sql * @param string $sql
* @param array $params * @param array $params
* @return PDOStatement * @return PDOStatement
* @throws Exception * @throws Exception
*/ */
private function queryPrev(string $sql, array $params = []): PDOStatement private function queryPrev(string $sql, array $params = []): PDOStatement
{ {
$this->_last = time(); $this->_last = time();
if (($statement = $this->_pdo()->query($sql)) === false) { try {
throw new Exception($this->_pdo()->errorInfo()[1]); if (($statement = $this->_pdo()->query($sql)) === false) {
} throw new Exception($this->_pdo()->errorInfo()[1]);
return $this->bindValue($statement, $params); }
} return $this->bindValue($statement, $params);
} catch (\Throwable $throwable) {
if (str_contains($throwable->getMessage(), 'MySQL server has gone away')) {
$this->pdo = null;
return $this->queryPrev($sql, $params);
}
throw new Exception($throwable->getMessage());
}
}
/** /**
* @param PDOStatement $statement * @param PDOStatement $statement
* @param array $params * @param array $params
* @return PDOStatement * @return PDOStatement
*/ */
private function bindValue(PDOStatement $statement, array $params = []): PDOStatement private function bindValue(PDOStatement $statement, array $params = []): PDOStatement
{ {
if (empty($params)) return $statement; if (empty($params)) return $statement;
foreach ($params as $key => $param) { foreach ($params as $key => $param) {
$statement->bindValue($key, $param); $statement->bindValue($key, $param);
} }
return $statement; return $statement;
} }
/** /**
* @param string $sql * @param string $sql
* @param $isInsert * @param $isInsert
* @param array $params * @param array $params
* @return int * @return int
* @throws Exception * @throws Exception
*/ */
public function execute(string $sql, $isInsert, array $params = []): int public function execute(string $sql, $isInsert, array $params = []): int
{ {
$this->_last = time(); $this->_last = time();
if (!(($prepare = $this->_pdo()->prepare($sql)) instanceof PDOStatement)) { if (!(($prepare = $this->_pdo()->prepare($sql)) instanceof PDOStatement)) {
throw new Exception($prepare->errorInfo()[2] ?? static::DB_ERROR_MESSAGE); throw new Exception($prepare->errorInfo()[2] ?? static::DB_ERROR_MESSAGE);
} }
if ($prepare->execute($params) === false) { if ($prepare->execute($params) === false) {
throw new Exception($prepare->errorInfo()[2] ?? static::DB_ERROR_MESSAGE); throw new Exception($prepare->errorInfo()[2] ?? static::DB_ERROR_MESSAGE);
} }
$result = 1; $result = 1;
if ($isInsert) { if ($isInsert) {
$result = (int)$this->_pdo()->lastInsertId(); $result = (int)$this->_pdo()->lastInsertId();
} }
$prepare->closeCursor(); $prepare->closeCursor();
return $result; return $result;
} }
/** /**
* @return \PDO * @return \PDO
*/ */
public function _pdo(): \PDO public function _pdo(): \PDO
{ {
if ($this->_timer === -1) { if ($this->_timer === -1) {
$this->heartbeat_check(); $this->heartbeat_check();
} }
if (!($this->pdo instanceof \PDO)) { if (!($this->pdo instanceof \PDO)) {
$this->pdo = $this->newClient(); $this->pdo = $this->newClient();
} }
return $this->pdo; return $this->pdo;
} }
/** /**
* @return \PDO * @return \PDO
*/ */
private function newClient(): \PDO private function newClient(): \PDO
{ {
$link = new \PDO('mysql:dbname=' . $this->dbname . ';host=' . $this->cds, $this->username, $this->password, [ $link = new \PDO('mysql:dbname=' . $this->dbname . ';host=' . $this->cds, $this->username, $this->password, [
\PDO::ATTR_EMULATE_PREPARES => false, \PDO::ATTR_EMULATE_PREPARES => false,
\PDO::ATTR_CASE => \PDO::CASE_NATURAL, \PDO::ATTR_CASE => \PDO::CASE_NATURAL,
\PDO::ATTR_TIMEOUT => 60, \PDO::ATTR_TIMEOUT => 60,
\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . $this->chatset \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . $this->chatset
]); ]);
$link->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $link->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$link->setAttribute(\PDO::ATTR_STRINGIFY_FETCHES, false); $link->setAttribute(\PDO::ATTR_STRINGIFY_FETCHES, false);
$link->setAttribute(\PDO::ATTR_ORACLE_NULLS, \PDO::NULL_EMPTY_STRING); $link->setAttribute(\PDO::ATTR_ORACLE_NULLS, \PDO::NULL_EMPTY_STRING);
return $link; return $link;
} }
} }