feat: 增加内置节点注册任务;优化定时器代码结构;

This commit is contained in:
augushong
2025-08-23 23:22:52 +08:00
parent 716e56b8dc
commit a9d1429000
7 changed files with 163 additions and 97 deletions

View File

@@ -2,7 +2,8 @@
namespace base\common\service;
use app\common\model\SystemHost;
use app\admin\model\SystemHost;
use app\common\tools\PathTools;
use think\facade\App;
use think\facade\Log;
@@ -13,22 +14,22 @@ use think\facade\Log;
class HostServiceBase
{
/**
* 获取当前主机的唯一ID。
* 获取当前节点的唯一ID。
* 该方法确保即使在多个具有相同hostname的隔离网络中ID也是唯一的。
* 它会在首次运行时生成一个ID并持久化到本地文件中以保证重启后ID不变。
*
* @return string
*/
public static function getHostId(): string
public static function getNodeId(): string
{
// 定义一个持久化存储ID的文件路径 (runtime目录在部署时通常是可写的)
$idFilePath = App::getRuntimePath() . 'host_id.lock';
$idFilePath = PathTools::tempBuildPath('node_id.lock');
// 1. 如果ID文件已存在直接读取并返回
if (file_exists($idFilePath)) {
$hostId = file_get_contents($idFilePath);
if (!empty($hostId)) {
return trim($hostId);
$nodeId = file_get_contents($idFilePath);
if (!empty($nodeId)) {
return trim($nodeId);
}
}
@@ -36,58 +37,55 @@ class HostServiceBase
// 格式: hostname-8位随机字符串
$hostname = gethostname() ?: 'unknown_host';
$uniqueSuffix = substr(md5(uniqid((string) mt_rand(), true)), 0, 8);
$newHostId = "{$hostname}-{$uniqueSuffix}";
$newNodeId = "{$hostname}-{$uniqueSuffix}";
// 3. 将新ID写入文件以便下次启动时使用
try {
file_put_contents($idFilePath, $newHostId);
file_put_contents($idFilePath, $newNodeId);
} catch (\Exception $e) {
Log::error('无法写入主机ID文件: ' . $idFilePath . ' - ' . $e->getMessage());
Log::error('无法写入节点ID文件: ' . $idFilePath . ' - ' . $e->getMessage());
}
return $newHostId;
return $newNodeId;
}
/**
* 主机注册与心跳更新
* 这是一个原子操作,如果主机不存在则创建,如果存在则更新。
* 节点注册与心跳更新
* 这是一个原子操作,如果节点不存在则创建,如果存在则更新。
*/
public static function heartbeat()
{
$hostId = self::getHostId();
if (empty($hostId)) {
Log::error('无法获取当前主机ID心跳更新失败');
$nodeId = self::getNodeId();
if (empty($nodeId)) {
Log::error('无法获取当前节点ID心跳更新失败');
return;
}
try {
// findOrEmpty可以避免查询不到时抛出异常
$host = SystemHost::where('host_id', $hostId)->findOrEmpty();
$host = SystemHost::where('node_id', $nodeId)->find();
// 收集动态性能指标
$data = self::collectHostMetrics();
if ($host->isEmpty()) {
// 首次注册:补充一次性的静态信息
$data['host_id'] = $hostId;
$data = array_merge($data, self::collectStaticInfo());
SystemHost::create($data);
Log::info("主机 [{$hostId}] 已成功注册并上线。");
} else {
// 后续心跳:仅更新动态信息
$host->save($data);
$data = self::collectHostInfo();
if (empty($host)) {
$host = new SystemHost();
// 首次注册
$data['node_id'] = $nodeId;
Log::info("节点 [{$nodeId}] 已成功注册并上线。");
}
$host->save($data);
} catch (\Exception $e) {
Log::error("主机 [{$hostId}] 心跳更新失败: " . $e->getMessage());
throw $e;
Log::error("节点 [{$nodeId}] 心跳更新失败: " . $e->getMessage());
Log::error($e);
}
}
/**
* 收集主机的动态性能指标 (不依赖任何外部系统命令).
* 收集主机的性能指标和静态信息 (不依赖任何外部系统命令).
* @return array
*/
public static function collectHostMetrics(): array
public static function collectHostInfo(): array
{
// 获取CPU平均负载 (如果函数存在且未被禁用)
$cpuLoad = null;
@@ -97,6 +95,7 @@ class HostServiceBase
}
return [
// 动态指标
'status' => 1,
'last_heartbeat_at' => date('Y-m-d H:i:s'),
'ip_address' => gethostbyname(gethostname()),
@@ -104,16 +103,8 @@ class HostServiceBase
'memory_usage' => memory_get_usage(), // false: 获取脚本自身内存占用
'disk_free' => disk_free_space(App::getRootPath()),
'disk_total' => disk_total_space(App::getRootPath()),
];
}
/**
* 收集主机的静态信息 (仅在首次注册时调用).
* @return array
*/
public static function collectStaticInfo(): array
{
return [
// 静态信息
'os_info' => php_uname(),
'php_version' => PHP_VERSION,
];