mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-08-30 12:45:32 +08:00
TimerServiceBase syncConfig 不覆盖 DB concurrency(NULL=继承代码默认)。TimerControllerBase cap 改 >= 语义,放行 concurrency_id < cap 的 0-indexed 分片。 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
120 lines
3.9 KiB
PHP
120 lines
3.9 KiB
PHP
<?php
|
||
|
||
namespace base\common\controller;
|
||
|
||
use app\admin\model\SystemTimerLog;
|
||
use app\common\controller\ToolsController;
|
||
use think\facade\Cache;
|
||
|
||
class TimerControllerBase extends ToolsController
|
||
{
|
||
protected $frequency = null;
|
||
|
||
/**
|
||
* 最大并发上限 cap.
|
||
* 放行 concurrency_id < $concurrency 的请求(0-indexed 分片,合法 id ∈ [0, cap-1]);
|
||
* 放行 concurrency_count <= $concurrency 的请求(并发总数声明,合法 ∈ [1, cap])。
|
||
* DB 实际并发通过 T6 有效域钳制 = min(DB concurrency, 控制器 cap, max_handles 预算),
|
||
* 动态扩容时高 concurrency_id 只要 < cap 就不会被误杀。
|
||
* 子类按任务实际承载力自行设置(默认 1 = 单分片,只允许 id=0)。
|
||
*/
|
||
protected $concurrency = 1;
|
||
|
||
protected $concurrencyId = 0;
|
||
|
||
protected $hostId = null;
|
||
|
||
public function initialize()
|
||
{
|
||
parent::initialize();
|
||
|
||
$concurrency_id = $this->request->param('concurrency_id', 0);
|
||
// cap 防御:0-indexed 分片号,合法 id ∈ [0, cap-1];id >= cap 视为越界(超 cap 的分片不被本控制器承载)
|
||
if ($concurrency_id >= $this->concurrency) {
|
||
$this->error('concurrency id error');
|
||
}
|
||
$this->concurrencyId = $concurrency_id;
|
||
|
||
$concurrency_count = $this->request->param('concurrency_count', 1);
|
||
// cap 防御:并发总数声明,合法 ∈ [1, cap];count > cap 视为超限(DB concurrency 经 T6 钳制后不会超过 cap)
|
||
if ($concurrency_count > $this->concurrency) {
|
||
$this->error('concurrency count error');
|
||
}
|
||
|
||
$this->hostId = $this->request->param('host_id', '');
|
||
|
||
if (is_int($this->frequency)) {
|
||
$this->protectVisit($this->frequency);
|
||
}
|
||
}
|
||
|
||
protected function protectVisit(int $frequency)
|
||
{
|
||
$cache_tag = 'timer_protect';
|
||
|
||
$cache_key = 'timer_protect_' . md5($this->request->url());
|
||
|
||
$last_exec_time = Cache::get($cache_key, 0);
|
||
|
||
if ($last_exec_time >= time() - $frequency) {
|
||
return $this->error('请不要频繁请求');
|
||
}
|
||
|
||
Cache::tag($cache_tag)->set($cache_key, time());
|
||
}
|
||
|
||
public function logStart(string $taskName): int
|
||
{
|
||
$data = [
|
||
'task_name' => $taskName,
|
||
'node_id' => $this->hostId,
|
||
'run_type' => '',
|
||
'start_time' => time(),
|
||
'end_time' => 0,
|
||
'duration' => 0,
|
||
'status' => 'running',
|
||
'error_message' => null,
|
||
'result' => null,
|
||
'concurrency_id' => $this->concurrencyId,
|
||
'create_time' => time(),
|
||
];
|
||
$log = SystemTimerLog::create($data);
|
||
return $log->id;
|
||
}
|
||
|
||
public function logEnd(int $logId, string $status = 'success', ?string $errorMessage = null, ?string $result = null): void
|
||
{
|
||
$log = SystemTimerLog::find($logId);
|
||
if ($log) {
|
||
$endTime = time();
|
||
$log->end_time = $endTime;
|
||
$log->duration = ($endTime - $log->start_time) * 1000; // ms
|
||
$log->status = $status;
|
||
$log->error_message = $errorMessage;
|
||
$log->result = $result;
|
||
$log->save();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 日志包裹入口:自动记录 do() 的执行过程和返回值.
|
||
* 子类仍实现 do(),URL 路由从 /do 改为 /execute 触发此方法.
|
||
*/
|
||
public function execute()
|
||
{
|
||
$taskName = $this->request->param('task_name', '');
|
||
$logId = $this->logStart($taskName);
|
||
|
||
try {
|
||
$result = $this->do();
|
||
$resultStr = is_string($result) ? $result : json_encode($result, JSON_UNESCAPED_UNICODE);
|
||
$this->logEnd($logId, 'success', null, $resultStr);
|
||
|
||
return $result;
|
||
} catch (\Throwable $e) {
|
||
$this->logEnd($logId, 'error', $e->getMessage());
|
||
throw $e;
|
||
}
|
||
}
|
||
}
|