mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-07-01 15:32:48 +08:00
feat(timer): 新增配置同步到数据库及主节点选举
T5: TimerServiceBase.syncConfigToDatabase() - syncs task config to DB
T6: HostServiceBase - auto master election, stale node detection,
getMasterNode/setMasterNode methods
This commit is contained in:
12
app/admin/model/SystemTimerConfig.php
Normal file
12
app/admin/model/SystemTimerConfig.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\admin\model;
|
||||||
|
|
||||||
|
use think\Model;
|
||||||
|
|
||||||
|
class SystemTimerConfig extends Model
|
||||||
|
{
|
||||||
|
protected $name = 'system_timer_config';
|
||||||
|
|
||||||
|
protected $autoWriteTimestamp = true;
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ use app\common\model\TimeModel;
|
|||||||
* @property string $node_id 节点ID
|
* @property string $node_id 节点ID
|
||||||
* @property string $ip_address IP地址
|
* @property string $ip_address IP地址
|
||||||
* @property int $status 状态 0:离线,1:在线
|
* @property int $status 状态 0:离线,1:在线
|
||||||
|
* @property int $is_master 是否主节点 0:否,1:是
|
||||||
* @property string $last_heartbeat_at 最后心跳时间
|
* @property string $last_heartbeat_at 最后心跳时间
|
||||||
* @property string $os_info 系统信息
|
* @property string $os_info 系统信息
|
||||||
* @property string $php_version PHP版本
|
* @property string $php_version PHP版本
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ class TimerBase extends Command
|
|||||||
$output->writeln('站点域名:' . $host);
|
$output->writeln('站点域名:' . $host);
|
||||||
$site_host = parse_url($host, PHP_URL_HOST);
|
$site_host = parse_url($host, PHP_URL_HOST);
|
||||||
|
|
||||||
|
// 同步配置到数据库
|
||||||
|
TimerService::syncConfigToDatabase();
|
||||||
|
|
||||||
// 设置配置的任务
|
// 设置配置的任务
|
||||||
$timer_service = new TimerService();
|
$timer_service = new TimerService();
|
||||||
$request_list = $timer_service->generateAllRequestList();
|
$request_list = $timer_service->generateAllRequestList();
|
||||||
|
|||||||
@@ -75,6 +75,23 @@ class HostServiceBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
$host->save($data);
|
$host->save($data);
|
||||||
|
|
||||||
|
// 主节点自动选举:若无在线主节点,当前节点自动成为主节点
|
||||||
|
$master = SystemHost::where('is_master', 1)->where('status', 1)->find();
|
||||||
|
if (empty($master)) {
|
||||||
|
$host->is_master = 1;
|
||||||
|
$host->save();
|
||||||
|
Log::info("节点 [{$nodeId}] 自动当选为主节点。");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过期节点检测:超过90秒无心跳的节点标记为离线
|
||||||
|
$staleTime = date('Y-m-d H:i:s', time() - 90);
|
||||||
|
$staleCount = SystemHost::where('status', 1)
|
||||||
|
->where('last_heartbeat_at', '<', $staleTime)
|
||||||
|
->update(['status' => 0]);
|
||||||
|
if ($staleCount > 0) {
|
||||||
|
Log::info("已将 {$staleCount} 个过期节点标记为离线。");
|
||||||
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
throw $e;
|
throw $e;
|
||||||
Log::error("节点 [{$nodeId}] 心跳更新失败: " . $e->getMessage());
|
Log::error("节点 [{$nodeId}] 心跳更新失败: " . $e->getMessage());
|
||||||
@@ -110,4 +127,38 @@ class HostServiceBase
|
|||||||
'php_version' => PHP_VERSION,
|
'php_version' => PHP_VERSION,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前主节点的node_id.
|
||||||
|
*
|
||||||
|
* @return string|null 返回主节点ID,无主节点时返回null
|
||||||
|
*/
|
||||||
|
public static function getMasterNode(): ?string
|
||||||
|
{
|
||||||
|
$master = SystemHost::where('is_master', 1)->where('status', 1)->find();
|
||||||
|
return $master ? $master->node_id : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动切换主节点.
|
||||||
|
*
|
||||||
|
* @param string $nodeId 目标节点的node_id
|
||||||
|
* @return bool 切换成功返回true,节点不存在返回false
|
||||||
|
*/
|
||||||
|
public static function setMasterNode(string $nodeId): bool
|
||||||
|
{
|
||||||
|
// 先清除所有主节点标记
|
||||||
|
SystemHost::where('is_master', 1)->update(['is_master' => 0]);
|
||||||
|
|
||||||
|
// 设置新主节点
|
||||||
|
$host = SystemHost::where('node_id', $nodeId)->find();
|
||||||
|
if ($host) {
|
||||||
|
$host->is_master = 1;
|
||||||
|
$host->save();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,69 @@
|
|||||||
|
|
||||||
namespace base\common\service;
|
namespace base\common\service;
|
||||||
|
|
||||||
|
use app\admin\model\SystemTimerConfig;
|
||||||
use app\common\model\VirtualModel;
|
use app\common\model\VirtualModel;
|
||||||
|
|
||||||
class TimerServiceBase
|
class TimerServiceBase
|
||||||
{
|
{
|
||||||
protected $taskList = null;
|
protected $taskList = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将配置文件中的定时任务同步到数据库.
|
||||||
|
* 仅新增不存在的记录,标记孤立记录,不覆盖 run_type/status.
|
||||||
|
*/
|
||||||
|
public static function syncConfigToDatabase(): void
|
||||||
|
{
|
||||||
|
$config_list = include app_file_path('common/command/timer/config.php');
|
||||||
|
|
||||||
|
// 提取唯一的任务名称(并发扩展会产生同名配置,需去重)
|
||||||
|
$config_names = [];
|
||||||
|
foreach ($config_list as $config_item) {
|
||||||
|
$item = static::initConfigItem($config_item);
|
||||||
|
$name = (string) $item['name'];
|
||||||
|
if ($name !== '' && !in_array($name, $config_names, true)) {
|
||||||
|
$config_names[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询已有记录
|
||||||
|
$existing = SystemTimerConfig::column('task_name');
|
||||||
|
$now = time();
|
||||||
|
|
||||||
|
// 新增:配置中有但数据库中没有的
|
||||||
|
$new_names = array_diff($config_names, $existing);
|
||||||
|
foreach ($new_names as $name) {
|
||||||
|
SystemTimerConfig::create([
|
||||||
|
'task_name' => $name,
|
||||||
|
'run_type' => 'auto',
|
||||||
|
'status' => 1,
|
||||||
|
'is_synced' => 1,
|
||||||
|
'create_time' => $now,
|
||||||
|
'update_time' => $now,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 已存在且仍在配置中:仅更新 is_synced=1,不动 run_type/status
|
||||||
|
$still_exists = array_intersect($existing, $config_names);
|
||||||
|
foreach ($still_exists as $name) {
|
||||||
|
SystemTimerConfig::where('task_name', $name)
|
||||||
|
->update([
|
||||||
|
'is_synced' => 1,
|
||||||
|
'update_time' => $now,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 已存在但不在配置中:标记为孤立
|
||||||
|
$orphaned = array_diff($existing, $config_names);
|
||||||
|
foreach ($orphaned as $name) {
|
||||||
|
SystemTimerConfig::where('task_name', $name)
|
||||||
|
->update([
|
||||||
|
'is_synced' => 0,
|
||||||
|
'update_time' => $now,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function generateAllTaskInstanceList()
|
public static function generateAllTaskInstanceList()
|
||||||
{
|
{
|
||||||
$config_list = include app_file_path('common/command/timer/config.php');
|
$config_list = include app_file_path('common/command/timer/config.php');
|
||||||
|
|||||||
Reference in New Issue
Block a user