Files
ulthon_admin/extend/base/common/service/HostServiceBase.php
2025-08-23 23:24:45 +08:00

114 lines
3.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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