Files
ulthon_admin/extend/base/common/service/HostServiceBase.php
2025-08-23 22:44:47 +08:00

122 lines
3.9 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\common\model\SystemHost;
use think\facade\App;
use think\facade\Log;
/**
* 主机(节点)服务 - 基础类
* 用于注册、上报主机心跳及性能指标.
*/
class HostServiceBase
{
/**
* 获取当前主机的唯一ID。
* 该方法确保即使在多个具有相同hostname的隔离网络中ID也是唯一的。
* 它会在首次运行时生成一个ID并持久化到本地文件中以保证重启后ID不变。
*
* @return string
*/
public static function getHostId(): string
{
// 定义一个持久化存储ID的文件路径 (runtime目录在部署时通常是可写的)
$idFilePath = App::getRuntimePath() . 'host_id.lock';
// 1. 如果ID文件已存在直接读取并返回
if (file_exists($idFilePath)) {
$hostId = file_get_contents($idFilePath);
if (!empty($hostId)) {
return trim($hostId);
}
}
// 2. 如果文件不存在或为空生成新的唯一ID
// 格式: hostname-8位随机字符串
$hostname = gethostname() ?: 'unknown_host';
$uniqueSuffix = substr(md5(uniqid((string) mt_rand(), true)), 0, 8);
$newHostId = "{$hostname}-{$uniqueSuffix}";
// 3. 将新ID写入文件以便下次启动时使用
try {
file_put_contents($idFilePath, $newHostId);
} catch (\Exception $e) {
Log::error('无法写入主机ID文件: ' . $idFilePath . ' - ' . $e->getMessage());
}
return $newHostId;
}
/**
* 主机注册与心跳更新
* 这是一个原子操作,如果主机不存在则创建,如果存在则更新。
*/
public static function heartbeat()
{
$hostId = self::getHostId();
if (empty($hostId)) {
Log::error('无法获取当前主机ID心跳更新失败');
return;
}
try {
// findOrEmpty可以避免查询不到时抛出异常
$host = SystemHost::where('host_id', $hostId)->findOrEmpty();
// 收集动态性能指标
$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);
}
} catch (\Exception $e) {
Log::error("主机 [{$hostId}] 心跳更新失败: " . $e->getMessage());
}
}
/**
* 收集主机的动态性能指标 (不依赖任何外部系统命令).
* @return array
*/
public static function collectHostMetrics(): 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()),
];
}
/**
* 收集主机的静态信息 (仅在首次注册时调用).
* @return array
*/
public static function collectStaticInfo(): array
{
return [
'os_info' => php_uname(),
'php_version' => PHP_VERSION,
];
}
}