65 lines
1.5 KiB
PHP
65 lines
1.5 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace app\common\service;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 定时同步全局互斥锁,防止每分钟 cron 重叠执行打满 Node 队列
|
||
|
|
*/
|
||
|
|
class SplitCronLockService
|
||
|
|
{
|
||
|
|
private const LOCK_FILE = 'cron.lock';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 尝试获取全局 cron 锁
|
||
|
|
*/
|
||
|
|
public function acquire(): bool
|
||
|
|
{
|
||
|
|
$path = $this->lockPath();
|
||
|
|
if ($this->isStaleLock($path)) {
|
||
|
|
@unlink($path);
|
||
|
|
}
|
||
|
|
if (is_file($path)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
$payload = json_encode([
|
||
|
|
'pid' => getmypid(),
|
||
|
|
'time' => time(),
|
||
|
|
], JSON_UNESCAPED_UNICODE);
|
||
|
|
$written = @file_put_contents($path, $payload, LOCK_EX);
|
||
|
|
return $written !== false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 释放全局 cron 锁
|
||
|
|
*/
|
||
|
|
public function release(): void
|
||
|
|
{
|
||
|
|
$path = $this->lockPath();
|
||
|
|
if (is_file($path)) {
|
||
|
|
@unlink($path);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function lockPath(): string
|
||
|
|
{
|
||
|
|
$runtime = defined('RUNTIME_PATH') ? RUNTIME_PATH : (dirname(__DIR__, 3) . '/runtime/');
|
||
|
|
$dir = $runtime . 'split_ticket_sync/';
|
||
|
|
if (!is_dir($dir)) {
|
||
|
|
@mkdir($dir, 0755, true);
|
||
|
|
}
|
||
|
|
return $dir . self::LOCK_FILE;
|
||
|
|
}
|
||
|
|
|
||
|
|
private function isStaleLock(string $path): bool
|
||
|
|
{
|
||
|
|
if (!is_file($path)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
$mtime = (int) @filemtime($path);
|
||
|
|
$ttl = SplitSyncConfigService::getCronLockTtlSeconds();
|
||
|
|
return $mtime > 0 && (time() - $mtime) > $ttl;
|
||
|
|
}
|
||
|
|
}
|