mirror of
https://github.com/fastapi-practices/fastapi-best-architecture.git
synced 2026-09-21 13:12:24 +00:00
Update redis and server monitor implementations (#1000)
* Update redis and server monitor implementations * Fix server disk information
This commit is contained in:
@@ -1,16 +1,40 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from backend.common.response.response_schema import ResponseModel, response_base
|
||||
from backend.app.admin.schema.monitor import RedisCommandStat, RedisMonitorInfo, RedisServerInfo
|
||||
from backend.common.response.response_schema import ResponseSchemaModel, response_base
|
||||
from backend.common.security.jwt import DependsJwtAuth
|
||||
from backend.utils.redis_info import redis_info
|
||||
from backend.database.redis import redis_client
|
||||
from backend.utils.format import fmt_seconds
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get('', summary='redis 监控', dependencies=[DependsJwtAuth])
|
||||
async def get_redis_info() -> ResponseModel:
|
||||
data = {
|
||||
'info': await redis_info.get_info(),
|
||||
'stats': await redis_info.get_stats(),
|
||||
}
|
||||
async def get_redis_info() -> ResponseSchemaModel[RedisMonitorInfo]:
|
||||
info = await redis_client.info()
|
||||
db_size = await redis_client.dbsize()
|
||||
|
||||
uptime_formatted = fmt_seconds(int(info.get('uptime_in_seconds', 0)))
|
||||
|
||||
server_info = RedisServerInfo(
|
||||
redis_version=str(info.get('redis_version', '')),
|
||||
redis_mode=str(info.get('redis_mode', '')),
|
||||
os=str(info.get('os', '')),
|
||||
arch_bits=str(info.get('arch_bits', '')),
|
||||
tcp_port=str(info.get('tcp_port', '')),
|
||||
uptime_in_seconds=uptime_formatted,
|
||||
connected_clients=str(info.get('connected_clients', '')),
|
||||
used_memory_human=str(info.get('used_memory_human', '')),
|
||||
used_memory_peak_human=str(info.get('used_memory_peak_human', '')),
|
||||
maxmemory_human=str(info.get('maxmemory_human', '0B')),
|
||||
keys_num=str(db_size),
|
||||
)
|
||||
|
||||
command_stats = await redis_client.info('commandstats')
|
||||
stats_list = []
|
||||
for key, value in command_stats.items():
|
||||
if isinstance(value, dict):
|
||||
stats_list.append(RedisCommandStat(name=key.split('_')[-1], value=str(value.get('calls', '0'))))
|
||||
|
||||
data = RedisMonitorInfo(info=server_info, stats=stats_list)
|
||||
return response_base.success(data=data)
|
||||
|
||||
@@ -1,21 +1,134 @@
|
||||
import os
|
||||
import platform
|
||||
import socket
|
||||
import sys
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import timezone as tz
|
||||
|
||||
import psutil
|
||||
|
||||
from fastapi import APIRouter
|
||||
from starlette.concurrency import run_in_threadpool
|
||||
|
||||
from backend.common.response.response_schema import ResponseModel, response_base
|
||||
from backend.app.admin.schema.monitor import (
|
||||
CpuInfo,
|
||||
DiskInfo,
|
||||
MemInfo,
|
||||
ServerMonitorInfo,
|
||||
ServiceInfo,
|
||||
SysInfo,
|
||||
)
|
||||
from backend.common.response.response_schema import ResponseSchemaModel, response_base
|
||||
from backend.common.security.jwt import DependsJwtAuth
|
||||
from backend.utils.server_info import server_info
|
||||
from backend.utils.format import fmt_bytes, fmt_seconds
|
||||
from backend.utils.timezone import timezone
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get('', summary='server 监控', dependencies=[DependsJwtAuth])
|
||||
async def get_server_info() -> ResponseModel:
|
||||
data = {
|
||||
# 扔到线程池,避免阻塞
|
||||
'cpu': await run_in_threadpool(server_info.get_cpu_info),
|
||||
'mem': await run_in_threadpool(server_info.get_mem_info),
|
||||
'sys': await run_in_threadpool(server_info.get_sys_info),
|
||||
'disk': await run_in_threadpool(server_info.get_disk_info),
|
||||
'service': await run_in_threadpool(server_info.get_service_info),
|
||||
}
|
||||
async def get_server_info() -> ResponseSchemaModel[ServerMonitorInfo]: # noqa: C901
|
||||
def get_all_info() -> ServerMonitorInfo: # noqa: C901
|
||||
# CPU 信息
|
||||
cpu_data = {
|
||||
'usage': round(psutil.cpu_percent(interval=0.1), 2),
|
||||
'logical_num': psutil.cpu_count(logical=True) or 0,
|
||||
'physical_num': psutil.cpu_count(logical=False) or 0,
|
||||
'max_freq': 0.0,
|
||||
'min_freq': 0.0,
|
||||
'current_freq': 0.0,
|
||||
}
|
||||
|
||||
try:
|
||||
if hasattr(psutil, 'cpu_freq'):
|
||||
cpu_freq = psutil.cpu_freq()
|
||||
if cpu_freq:
|
||||
cpu_data.update({
|
||||
'max_freq': round(cpu_freq.max, 2),
|
||||
'min_freq': round(cpu_freq.min, 2),
|
||||
'current_freq': round(cpu_freq.current, 2),
|
||||
})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
cpu = CpuInfo(**cpu_data)
|
||||
|
||||
# 内存信息
|
||||
mem = psutil.virtual_memory()
|
||||
gb_factor = 1024**3
|
||||
mem_info = MemInfo(
|
||||
total=round(mem.total / gb_factor, 2),
|
||||
used=round(mem.used / gb_factor, 2),
|
||||
free=round(mem.available / gb_factor, 2),
|
||||
usage=round(mem.percent, 2),
|
||||
)
|
||||
|
||||
# 系统信息
|
||||
hostname = socket.gethostname()
|
||||
ip = '127.0.0.1'
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
||||
s.settimeout(0.5)
|
||||
s.connect(('8.8.8.8', 80))
|
||||
ip = s.getsockname()[0]
|
||||
except (TimeoutError, socket.gaierror, OSError):
|
||||
pass
|
||||
sys_info = SysInfo(name=hostname, ip=ip, os=platform.system(), arch=platform.machine())
|
||||
|
||||
# 磁盘信息
|
||||
disk_list = []
|
||||
exclude_fstypes = {'overlay', 'overlay2', 'tmpfs', 'devtmpfs', 'shm', 'proc', 'sysfs', 'cgroup', 'cgroup2'}
|
||||
seen_devices = set()
|
||||
for partition in psutil.disk_partitions(all=False):
|
||||
# 跳过虚拟文件系统
|
||||
if partition.fstype.lower() in exclude_fstypes:
|
||||
continue
|
||||
# 跳过重复设备(同一设备的不同挂载点)
|
||||
if partition.device in seen_devices:
|
||||
continue
|
||||
try:
|
||||
usage = psutil.disk_usage(partition.mountpoint)
|
||||
if usage:
|
||||
seen_devices.add(partition.device)
|
||||
disk_list.append(
|
||||
DiskInfo(
|
||||
dir=partition.mountpoint,
|
||||
type=partition.fstype,
|
||||
device=partition.device,
|
||||
total=fmt_bytes(usage.total),
|
||||
free=fmt_bytes(usage.free),
|
||||
used=fmt_bytes(usage.used),
|
||||
usage=f'{usage.percent:.2f}%',
|
||||
)
|
||||
)
|
||||
except (PermissionError, OSError):
|
||||
continue
|
||||
|
||||
# 服务信息
|
||||
process = psutil.Process(os.getpid())
|
||||
proc_mem = process.memory_info()
|
||||
try:
|
||||
create_time = datetime.fromtimestamp(process.create_time(), tz=tz.utc)
|
||||
start_time = timezone.from_datetime(create_time)
|
||||
except (psutil.NoSuchProcess, OSError):
|
||||
start_time = timezone.now()
|
||||
|
||||
elapsed = fmt_seconds(round((timezone.now() - start_time).total_seconds()))
|
||||
|
||||
service = ServiceInfo(
|
||||
name='Python3',
|
||||
version=platform.python_version(),
|
||||
home=sys.executable,
|
||||
cpu_usage=f'{process.cpu_percent(interval=0.1):.2f}%',
|
||||
mem_vms=fmt_bytes(proc_mem.vms),
|
||||
mem_rss=fmt_bytes(proc_mem.rss),
|
||||
mem_free=fmt_bytes(proc_mem.vms - proc_mem.rss),
|
||||
startup=timezone.to_str(start_time),
|
||||
elapsed=elapsed,
|
||||
)
|
||||
|
||||
return ServerMonitorInfo(cpu=cpu, mem=mem_info, sys=sys_info, disk=disk_list, service=service)
|
||||
|
||||
data = await run_in_threadpool(get_all_info)
|
||||
return response_base.success(data=data)
|
||||
|
||||
Reference in New Issue
Block a user