From c926ce7db819796bf03b4c558f603a556d927945 Mon Sep 17 00:00:00 2001 From: qiminbao Date: Wed, 20 Aug 2025 16:52:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mvnw | 259 ++++++++++++++++++ mvnw.cmd | 149 ++++++++++ pom.xml | 163 +++++++++++ .../agentserver/AgentServerApplication.java | 13 + .../agentserver/controller/TcpController.java | 35 +++ .../agentserver/core/enums/MsgEnum.java | 23 ++ .../core/exception/code/ErrorCode.java | 15 + .../core/exception/code/GlobalErrorCode.java | 24 ++ .../core/exception/type/BaseException.java | 42 +++ .../core/exception/type/ServerException.java | 26 ++ .../agentserver/core/session/Session.java | 85 ++++++ .../core/session/SessionManager.java | 137 +++++++++ .../tongran/agentserver/core/vo/BaseBO.java | 20 ++ .../tongran/agentserver/core/vo/T0x94.java | 38 +++ .../agentserver/scheduler/CpuScheduler.java | 78 ++++++ .../agentserver/scheduler/DiskScheduler.java | 81 ++++++ .../scheduler/DockerScheduler.java | 81 ++++++ .../agentserver/scheduler/HeartScheduler.java | 79 ++++++ .../scheduler/MemoryScheduler.java | 79 ++++++ .../agentserver/scheduler/NetScheduler.java | 81 ++++++ .../agentserver/scheduler/PointScheduler.java | 81 ++++++ .../scheduler/SwitchBoardScheduler.java | 80 ++++++ .../agentserver/scheduler/SysScheduler.java | 78 ++++++ .../server/collect/cpu/CpuService.java | 7 + .../collect/cpu/impl/CpuServiceImpl.java | 165 +++++++++++ .../server/collect/disk/DiskService.java | 12 + .../collect/disk/impl/DiskServiceImpl.java | 132 +++++++++ .../server/collect/docker/DockerService.java | 9 + .../docker/impl/DockerServiceImpl.java | 114 ++++++++ .../server/collect/memory/MemoryService.java | 7 + .../memory/impl/MemoryServiceImpl.java | 228 +++++++++++++++ .../server/collect/net/NetService.java | 9 + .../collect/net/impl/NetServiceImpl.java | 160 +++++++++++ .../switchboard/SwitchBoardService.java | 9 + .../impl/SwitchBoardServiceImpl.java | 190 +++++++++++++ .../server/collect/system/SystemService.java | 8 + .../system/impl/SystemServiceImpl.java | 183 +++++++++++++ .../agentserver/server/collect/vo/CpuVO.java | 57 ++++ .../agentserver/server/collect/vo/DiskVO.java | 47 ++++ .../server/collect/vo/DockerVO.java | 41 +++ .../server/collect/vo/MemoryVO.java | 37 +++ .../agentserver/server/collect/vo/NetVO.java | 49 ++++ .../server/collect/vo/PointVO.java | 33 +++ .../server/collect/vo/SwitchBoardVO.java | 35 +++ .../server/collect/vo/SystemVO.java | 58 ++++ .../server/netty/NettyTcpClient.java | 127 +++++++++ .../server/netty/config/AgentNettyConfig.java | 16 ++ .../server/netty/handler/DecoderHandler.java | 55 ++++ .../netty/handler/NettyClientHandler.java | 214 +++++++++++++++ .../server/netty/model/Message.java | 37 +++ .../tongran/agentserver/utils/AgentUtil.java | 20 ++ .../tongran/agentserver/utils/AssertLog.java | 73 +++++ .../tongran/agentserver/utils/EscapeUtil.java | 227 +++++++++++++++ .../java/com/tongran/agentserver/utils/R.java | 79 ++++++ .../agentserver/utils/SwitchInfoFetcher.java | 172 ++++++++++++ src/main/resources/application-dev.yml | 25 ++ src/main/resources/application-prod.yml | 25 ++ src/main/resources/application.yml | 31 +++ src/main/resources/logback/logback-dev.xml | 116 ++++++++ src/main/resources/logback/logback-prod.xml | 115 ++++++++ .../AgentServerApplicationTests.java | 13 + 61 files changed, 4682 insertions(+) create mode 100644 mvnw create mode 100644 mvnw.cmd create mode 100644 pom.xml create mode 100644 src/main/java/com/tongran/agentserver/AgentServerApplication.java create mode 100644 src/main/java/com/tongran/agentserver/controller/TcpController.java create mode 100644 src/main/java/com/tongran/agentserver/core/enums/MsgEnum.java create mode 100644 src/main/java/com/tongran/agentserver/core/exception/code/ErrorCode.java create mode 100644 src/main/java/com/tongran/agentserver/core/exception/code/GlobalErrorCode.java create mode 100644 src/main/java/com/tongran/agentserver/core/exception/type/BaseException.java create mode 100644 src/main/java/com/tongran/agentserver/core/exception/type/ServerException.java create mode 100644 src/main/java/com/tongran/agentserver/core/session/Session.java create mode 100644 src/main/java/com/tongran/agentserver/core/session/SessionManager.java create mode 100644 src/main/java/com/tongran/agentserver/core/vo/BaseBO.java create mode 100644 src/main/java/com/tongran/agentserver/core/vo/T0x94.java create mode 100644 src/main/java/com/tongran/agentserver/scheduler/CpuScheduler.java create mode 100644 src/main/java/com/tongran/agentserver/scheduler/DiskScheduler.java create mode 100644 src/main/java/com/tongran/agentserver/scheduler/DockerScheduler.java create mode 100644 src/main/java/com/tongran/agentserver/scheduler/HeartScheduler.java create mode 100644 src/main/java/com/tongran/agentserver/scheduler/MemoryScheduler.java create mode 100644 src/main/java/com/tongran/agentserver/scheduler/NetScheduler.java create mode 100644 src/main/java/com/tongran/agentserver/scheduler/PointScheduler.java create mode 100644 src/main/java/com/tongran/agentserver/scheduler/SwitchBoardScheduler.java create mode 100644 src/main/java/com/tongran/agentserver/scheduler/SysScheduler.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/cpu/CpuService.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/cpu/impl/CpuServiceImpl.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/disk/DiskService.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/disk/impl/DiskServiceImpl.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/docker/DockerService.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/docker/impl/DockerServiceImpl.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/memory/MemoryService.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/memory/impl/MemoryServiceImpl.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/net/NetService.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/net/impl/NetServiceImpl.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/switchboard/SwitchBoardService.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/switchboard/impl/SwitchBoardServiceImpl.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/system/SystemService.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/system/impl/SystemServiceImpl.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/vo/CpuVO.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/vo/DiskVO.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/vo/DockerVO.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/vo/MemoryVO.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/vo/NetVO.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/vo/PointVO.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/vo/SwitchBoardVO.java create mode 100644 src/main/java/com/tongran/agentserver/server/collect/vo/SystemVO.java create mode 100644 src/main/java/com/tongran/agentserver/server/netty/NettyTcpClient.java create mode 100644 src/main/java/com/tongran/agentserver/server/netty/config/AgentNettyConfig.java create mode 100644 src/main/java/com/tongran/agentserver/server/netty/handler/DecoderHandler.java create mode 100644 src/main/java/com/tongran/agentserver/server/netty/handler/NettyClientHandler.java create mode 100644 src/main/java/com/tongran/agentserver/server/netty/model/Message.java create mode 100644 src/main/java/com/tongran/agentserver/utils/AgentUtil.java create mode 100644 src/main/java/com/tongran/agentserver/utils/AssertLog.java create mode 100644 src/main/java/com/tongran/agentserver/utils/EscapeUtil.java create mode 100644 src/main/java/com/tongran/agentserver/utils/R.java create mode 100644 src/main/java/com/tongran/agentserver/utils/SwitchInfoFetcher.java create mode 100644 src/main/resources/application-dev.yml create mode 100644 src/main/resources/application-prod.yml create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/logback/logback-dev.xml create mode 100644 src/main/resources/logback/logback-prod.xml create mode 100644 src/test/java/com/tongran/agentserver/AgentServerApplicationTests.java diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..19529dd --- /dev/null +++ b/mvnw @@ -0,0 +1,259 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..249bdf3 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,149 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..28c1da3 --- /dev/null +++ b/pom.xml @@ -0,0 +1,163 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.5.6 + + + com.tongran + agent-server + 0.0.1-SNAPSHOT + agent-server + agent-server + + + + + + + + + + + + + + + 1.8 + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-configuration-processor + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.projectlombok + lombok + true + + + + cn.hutool + hutool-all + 5.7.11 + + + + org.apache.commons + commons-lang3 + + + + io.netty + netty-all + 4.1.86.Final + + + + com.github.oshi + oshi-core + 6.4.4 + + + + com.github.docker-java + docker-java + 3.3.0 + + + + org.projectlombok + lombok + provided + + + + com.alibaba.fastjson2 + fastjson2 + 2.0.31 + + + + com.github.xiaoymin + knife4j-openapi3-spring-boot-starter + 4.5.0 + + + + + org.snmp4j + snmp4j + 2.8.9 + + + + org.slf4j + slf4j-api + 1.7.36 + + + org.slf4j + slf4j-simple + 1.7.36 + + + + + + + + + src/main/java + + **/*.xml + + + + + src/main/resources + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + true + + + + + + diff --git a/src/main/java/com/tongran/agentserver/AgentServerApplication.java b/src/main/java/com/tongran/agentserver/AgentServerApplication.java new file mode 100644 index 0000000..4e1097f --- /dev/null +++ b/src/main/java/com/tongran/agentserver/AgentServerApplication.java @@ -0,0 +1,13 @@ +package com.tongran.agentserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AgentServerApplication { + + public static void main(String[] args) { + SpringApplication.run(AgentServerApplication.class, args); + } + +} diff --git a/src/main/java/com/tongran/agentserver/controller/TcpController.java b/src/main/java/com/tongran/agentserver/controller/TcpController.java new file mode 100644 index 0000000..5fd858b --- /dev/null +++ b/src/main/java/com/tongran/agentserver/controller/TcpController.java @@ -0,0 +1,35 @@ +package com.tongran.agentserver.controller; + +import com.tongran.agentserver.scheduler.HeartScheduler; +import com.tongran.agentserver.server.netty.NettyTcpClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@RestController +public class TcpController { + + @Autowired + private NettyTcpClient nettyTcpClient; + + @Resource + private HeartScheduler heartScheduler; + + @GetMapping("/send") + public String sendMessage(@RequestParam String message) { + if (nettyTcpClient.isConnected()) { + nettyTcpClient.sendMessage(message); + return "消息已发送: " + message; + } + return "TCP连接未建立,消息发送失败"; + } + + @GetMapping("/task") + public String task(@RequestParam long intervalMillis) { + heartScheduler.updateInterval(intervalMillis); + return "TCP连接未建立,消息发送失败"; + } +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/core/enums/MsgEnum.java b/src/main/java/com/tongran/agentserver/core/enums/MsgEnum.java new file mode 100644 index 0000000..e4b4a52 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/core/enums/MsgEnum.java @@ -0,0 +1,23 @@ +package com.tongran.agentserver.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * 控制码 + */ +@Getter +@AllArgsConstructor +@NoArgsConstructor +public enum MsgEnum { + + 登录认证(0x01, 6, "0000"); + + private Integer value; + + private Integer start; + + private String serialNumber; + +} diff --git a/src/main/java/com/tongran/agentserver/core/exception/code/ErrorCode.java b/src/main/java/com/tongran/agentserver/core/exception/code/ErrorCode.java new file mode 100644 index 0000000..ddeeaf5 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/core/exception/code/ErrorCode.java @@ -0,0 +1,15 @@ +package com.tongran.agentserver.core.exception.code; + +import com.tongran.agentserver.utils.R; + +public interface ErrorCode { + + Integer getCode(); + + String getMsg(); + + default R toResult() { + return R.error(getMsg(), getCode()); + } + +} diff --git a/src/main/java/com/tongran/agentserver/core/exception/code/GlobalErrorCode.java b/src/main/java/com/tongran/agentserver/core/exception/code/GlobalErrorCode.java new file mode 100644 index 0000000..2f6912b --- /dev/null +++ b/src/main/java/com/tongran/agentserver/core/exception/code/GlobalErrorCode.java @@ -0,0 +1,24 @@ +package com.tongran.agentserver.core.exception.code; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum GlobalErrorCode implements ErrorCode { + + SUCCESS(200, "成功"), + + NOT_FOUND(404, "未找到相关资源"), + + PARAMETER_ERROR(410, "参数格式不正确"), + + NOT_SUPPORT_TYPE(412, "不支持的请求类型"), + + ERROR(500, "系统错误"); + + private final Integer code; + + private final String msg; + +} diff --git a/src/main/java/com/tongran/agentserver/core/exception/type/BaseException.java b/src/main/java/com/tongran/agentserver/core/exception/type/BaseException.java new file mode 100644 index 0000000..e0f0449 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/core/exception/type/BaseException.java @@ -0,0 +1,42 @@ +package com.tongran.agentserver.core.exception.type; + + +import com.tongran.agentserver.core.exception.code.ErrorCode; + +public class BaseException extends RuntimeException implements ErrorCode { + + private static final long serialVersionUID = 1966249840643379123L; + + private Integer code; + + private String msg; + + public BaseException() { + } + + public BaseException(String msg, Throwable cause) { + super(msg, cause); + } + + public BaseException(Integer code, String msg) { + super(msg); + this.code = code; + this.msg = msg; + } + + public BaseException(Integer code, String msg, Throwable cause) { + super(msg, cause); + this.code = code; + this.msg = msg; + } + + @Override + public Integer getCode() { + return code; + } + + @Override + public String getMsg() { + return msg; + } +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/core/exception/type/ServerException.java b/src/main/java/com/tongran/agentserver/core/exception/type/ServerException.java new file mode 100644 index 0000000..9199c7b --- /dev/null +++ b/src/main/java/com/tongran/agentserver/core/exception/type/ServerException.java @@ -0,0 +1,26 @@ +package com.tongran.agentserver.core.exception.type; + +import cn.hutool.core.util.StrUtil; +import com.tongran.agentserver.core.exception.code.ErrorCode; + +public class ServerException extends BaseException { + + private static final long serialVersionUID = -519977195881081739L; + + public ServerException(ErrorCode code) { + super(code.getCode(), code.getMsg()); + } + + public ServerException(Integer code, String msg) { + super(code, msg); + } + + public ServerException(String msg) { + super(500, msg); + } + + public ServerException(String msg, Object... arguments) { + super(500, StrUtil.format(msg, arguments)); + } + +} diff --git a/src/main/java/com/tongran/agentserver/core/session/Session.java b/src/main/java/com/tongran/agentserver/core/session/Session.java new file mode 100644 index 0000000..65c9948 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/core/session/Session.java @@ -0,0 +1,85 @@ +package com.tongran.agentserver.core.session; + +import io.netty.channel.ChannelHandlerContext; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * SESSION + * + */ +@Data +@Builder +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +public class Session implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * sessionId + */ + private String sessionId; + + /** + * 客户端唯一标识 + */ + private String clientId; + + /** + * Seesion额外参数 + */ + private ConcurrentHashMap extraMap; + + /** + * 是否鉴权 + */ + @Builder.Default + private boolean isAuthenticated = false; + + @Builder.Default + private long createTime = System.currentTimeMillis(); + + @Builder.Default + private long updateTime = System.currentTimeMillis(); + + /** + * 消息渠道 + */ + private ChannelHandlerContext channel; + + @Builder.Default + private AtomicInteger serialNo = new AtomicInteger(0); + + public static String buildSessionId(ChannelHandlerContext channel) { + return channel.channel().id().asLongText(); + } + + public static Session buildSession(ChannelHandlerContext channel, String clientId) { + return Session.builder().channel(channel).sessionId(buildSessionId(channel)).clientId(clientId).build(); + } + + /** + * 自生成流水号 + * + * @return + */ + public int nextSerialNo() { + int current; + int next; + do { + current = serialNo.get(); + next = current > 0xffff ? 0 : current; + } while (!serialNo.compareAndSet(current, next + 1)); + return next; + } + +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/core/session/SessionManager.java b/src/main/java/com/tongran/agentserver/core/session/SessionManager.java new file mode 100644 index 0000000..8c6d445 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/core/session/SessionManager.java @@ -0,0 +1,137 @@ +package com.tongran.agentserver.core.session; + + +import cn.hutool.cache.Cache; +import cn.hutool.cache.CacheUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import com.tongran.agentserver.core.exception.type.ServerException; +import com.tongran.agentserver.utils.AssertLog; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.util.Attribute; +import io.netty.util.AttributeKey; +import lombok.Data; +import org.apache.commons.lang3.StringUtils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Data +public class SessionManager { + + private static volatile SessionManager instance = null; + + // clientId-Session + private final Map sessionMap = new ConcurrentHashMap<>(); + + private final Cache sessionCache = CacheUtil.newTimedCache(10 * 60 * 1000); + + public static SessionManager getInstance() { + if (instance == null) { + synchronized (SessionManager.class) { + if (instance == null) { + instance = new SessionManager(); + } + } + } + return instance; + } + + public synchronized void put(String clientId, Session session) { + if (StringUtils.isNotBlank(session.getClientId())) { + sessionMap.put(session.getClientId(), session); + } + } + + public synchronized void remove(String clientId) { + if (StringUtils.isNotBlank(clientId)) { + sessionMap.remove(clientId); + } + } + + public synchronized void remove(Session session) { + if (session != null && StringUtils.isNotBlank(session.getClientId())) { + sessionMap.remove(session.getClientId(), session); + } + } + + public synchronized void remove(Channel channel) { + remove(getSessionByChannel(channel)); + } + + public Session getSessionById(String clientId) { + return sessionMap.get(clientId); + } + + public Session getSessionByChannel(Channel channel) { + Session session = new Session(); + sessionMap.values().forEach(s -> { + if (s.getChannel().channel() == channel) { + BeanUtil.copyProperties(s, session, true); + } + }); + return session; + } + + public boolean containsSession(String clientId) { + return sessionMap.containsKey(clientId); + } + + public boolean containsSession(Session session) { + return sessionMap.containsValue(session); + } + + public void setSessionCache(String clientId, Object value) { + sessionCache.put(clientId, value); + } + + public Object getSessionCache(String clientId) { + return sessionCache.get(clientId); + } + + public String client(ChannelHandlerContext ctx) { + Channel channel = ctx.channel(); + Session session = this.getSessionByChannel(channel); + if (ObjectUtil.isNotNull(session) && StringUtils.isNotBlank(session.getClientId())) { + return channel.remoteAddress().toString() + "/" + session.getClientId(); + } + return channel.remoteAddress().toString(); + } + + /** + * 根据channel生成流水号 + * + * @param channel + * @return + */ + public short getSerialNumber(Channel channel, AttributeKey serialNumber) { + Attribute flowIdAttr = channel.attr(serialNumber); + Short flowId = flowIdAttr.get(); + if (flowId == null) { + flowId = 0; + } else { + flowId++; + } + flowIdAttr.set(flowId); + return flowId; + } + + public void writeAndFlush(String clientId, Object msg) { + Session session = this.getSessionById(clientId); + if (ObjectUtil.isNotNull(session)) { + this.writeAndFlush(session.getChannel(), msg); + } else { + throw new ServerException(500, "终端:" + clientId + "离线或者不存在"); + } + } + + public void writeAndFlush(ChannelHandlerContext ctx, Object msg) { + ctx.writeAndFlush(msg).addListener(future -> { + if (!future.isSuccess()) { + AssertLog.error("消息发送失败:{}", future.cause()); + } + }); + } + +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/core/vo/BaseBO.java b/src/main/java/com/tongran/agentserver/core/vo/BaseBO.java new file mode 100644 index 0000000..b90f47d --- /dev/null +++ b/src/main/java/com/tongran/agentserver/core/vo/BaseBO.java @@ -0,0 +1,20 @@ +package com.tongran.agentserver.core.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +public class BaseBO { + + protected int cmd; //帧类型 + + protected String serialNumber; //序列号域 + + protected String pileNumber; // 桩编码 BCD 码 7 不足 7 位补 0 + +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/core/vo/T0x94.java b/src/main/java/com/tongran/agentserver/core/vo/T0x94.java new file mode 100644 index 0000000..371b472 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/core/vo/T0x94.java @@ -0,0 +1,38 @@ +package com.tongran.agentserver.core.vo; + +import lombok.*; +import lombok.experimental.SuperBuilder; + +/** + * 〈远程更新〉 + */ + +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class T0x94 extends BaseBO { + + @Builder.Default + private String len = "62"; + + private int pileType; // 桩类型 BIN 码 1 0x01:直流 0x02:交流 + + private int pilePower; // 桩功率 BIN 码 2 不足 2 位补零 + + private String upgradeServerAddress; // 升级服务器地址 ASCII 码 16 不足 16 位补零 + + private String upgradeServerPort; // 升级服务器端口 BIN 码 2 不足 2 位补零 + + private String account; // 用户名 ASCII 码 16 不足 16 位补零 + + private String password; // 密码 ASCII 码 16 不足 16 位补零 + + private String filePath; // 文件路径 ASCII 码 32 不足 32 位补零,文件路径名由平台定义 + + private int control; // 执行控制 BIN 码 1 0x01:立即执行 0x02:空闲执行 + + private int overtime; // 下载超时时间 BIN 码 1 单位:min + +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/scheduler/CpuScheduler.java b/src/main/java/com/tongran/agentserver/scheduler/CpuScheduler.java new file mode 100644 index 0000000..8052bfa --- /dev/null +++ b/src/main/java/com/tongran/agentserver/scheduler/CpuScheduler.java @@ -0,0 +1,78 @@ +package com.tongran.agentserver.scheduler; + +import com.alibaba.fastjson2.JSON; +import com.tongran.agentserver.server.collect.cpu.CpuService; +import com.tongran.agentserver.server.collect.vo.CpuVO; +import com.tongran.agentserver.server.netty.NettyTcpClient; +import com.tongran.agentserver.server.netty.model.Message; +import com.tongran.agentserver.utils.AgentUtil; +import com.tongran.agentserver.utils.AssertLog; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.concurrent.ScheduledFuture; + +@Component +@EnableScheduling +public class CpuScheduler { + + @Resource + private TaskScheduler taskScheduler; + + private ScheduledFuture scheduledTask; + + @Resource + private NettyTcpClient nettyTcpClient; + + @Resource + private CpuService cpuService; + + @PostConstruct + public void init() { + // 等待12秒 + try { + Thread.sleep(12000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + startTask(180000); // 默认3分钟间隔启动 + } + + public void startTask(long intervalMillis) { + if (scheduledTask != null) { + scheduledTask.cancel(false); + } + Runnable task = () -> { + AssertLog.info("CPU信息包准备={}", LocalDateTime.now()); + // 业务逻辑 + if (nettyTcpClient.isConnected()) { + // 发送CPU信息包 + CpuVO cpuVO =cpuService.query(); + String data = JSON.toJSONString(cpuVO); + String clientId = AgentUtil.getMotherboardUUID(); + Message message = Message.builder().clientId(clientId).dataType("CPU").data(data).build(); + // 将对象转为 JSON 字符串 + String json = "agent-tcp:"+JSON.toJSONString(message)+"@tong-ran"; + AssertLog.info("发送CPU信息包={}",json); + nettyTcpClient.sendMessage(json); + } + }; + + scheduledTask = taskScheduler.scheduleAtFixedRate(task, intervalMillis); + } + + public void updateInterval(long newIntervalMillis) { + startTask(newIntervalMillis); + } + + public void stopTask() { + if (scheduledTask != null) { + scheduledTask.cancel(false); + scheduledTask = null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/scheduler/DiskScheduler.java b/src/main/java/com/tongran/agentserver/scheduler/DiskScheduler.java new file mode 100644 index 0000000..cf25ee5 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/scheduler/DiskScheduler.java @@ -0,0 +1,81 @@ +package com.tongran.agentserver.scheduler; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.tongran.agentserver.server.collect.disk.DiskService; +import com.tongran.agentserver.server.collect.vo.DiskVO; +import com.tongran.agentserver.server.netty.NettyTcpClient; +import com.tongran.agentserver.server.netty.model.Message; +import com.tongran.agentserver.utils.AgentUtil; +import com.tongran.agentserver.utils.AssertLog; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; +import java.util.concurrent.ScheduledFuture; + +@Component +@EnableScheduling +public class DiskScheduler { + + @Resource + private TaskScheduler taskScheduler; + + private ScheduledFuture scheduledTask; + + @Resource + private NettyTcpClient nettyTcpClient; + + @Resource + private DiskService diskService; + + @PostConstruct + public void init() { + // 等待27秒 + try { + Thread.sleep(27000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + startTask(300000); // 默认300秒间隔启动 + } + + public void startTask(long intervalMillis) { + if (scheduledTask != null) { + scheduledTask.cancel(false); + } + + Runnable task = () -> { + AssertLog.info("磁盘信息包准备={}", LocalDateTime.now()); + // 业务逻辑 + if (nettyTcpClient.isConnected()) { + // 发送磁盘信息包 + List list = diskService.queryDiskList(); + String data = JSONArray.toJSONString(list); + String clientId = AgentUtil.getMotherboardUUID(); + Message message = Message.builder().clientId(clientId).dataType("DISK").data(data).build(); + // 将对象转为 JSON 字符串 + String json = "agent-tcp:"+JSON.toJSONString(message)+"@tong-ran"; + AssertLog.info("发送磁盘信息包={}",json); + nettyTcpClient.sendMessage(json); + } + }; + + scheduledTask = taskScheduler.scheduleAtFixedRate(task, intervalMillis); + } + + public void updateInterval(long newIntervalMillis) { + startTask(newIntervalMillis); + } + + public void stopTask() { + if (scheduledTask != null) { + scheduledTask.cancel(false); + scheduledTask = null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/scheduler/DockerScheduler.java b/src/main/java/com/tongran/agentserver/scheduler/DockerScheduler.java new file mode 100644 index 0000000..b0d4146 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/scheduler/DockerScheduler.java @@ -0,0 +1,81 @@ +package com.tongran.agentserver.scheduler; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.tongran.agentserver.server.collect.docker.DockerService; +import com.tongran.agentserver.server.collect.vo.DockerVO; +import com.tongran.agentserver.server.netty.NettyTcpClient; +import com.tongran.agentserver.server.netty.model.Message; +import com.tongran.agentserver.utils.AgentUtil; +import com.tongran.agentserver.utils.AssertLog; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; +import java.util.concurrent.ScheduledFuture; + +@Component +@EnableScheduling +public class DockerScheduler { + + @Resource + private TaskScheduler taskScheduler; + + private ScheduledFuture scheduledTask; + + @Resource + private NettyTcpClient nettyTcpClient; + + @Resource + private DockerService dockerService; + +// @PostConstruct + public void init() { + // 等待42秒 + try { + Thread.sleep(42000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + startTask(180000); // 默认180秒间隔启动 + } + + public void startTask(long intervalMillis) { + if (scheduledTask != null) { + scheduledTask.cancel(false); + } + + Runnable task = () -> { + AssertLog.info("容器信息包准备={}", LocalDateTime.now()); + // 业务逻辑 + if (nettyTcpClient.isConnected()) { + // 发送容器包 + List list = dockerService.query(); + String data = JSONArray.toJSONString(list); + String clientId = AgentUtil.getMotherboardUUID(); + Message message = Message.builder().clientId(clientId).dataType("DOCKER").data(data).build(); + // 将对象转为 JSON 字符串 + String json = "agent-tcp:"+JSON.toJSONString(message)+"@tong-ran"; + AssertLog.info("发送容器信息包={}",json); + nettyTcpClient.sendMessage(json); + } + + }; + + scheduledTask = taskScheduler.scheduleAtFixedRate(task, intervalMillis); + } + + public void updateInterval(long newIntervalMillis) { + startTask(newIntervalMillis); + } + + public void stopTask() { + if (scheduledTask != null) { + scheduledTask.cancel(false); + scheduledTask = null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/scheduler/HeartScheduler.java b/src/main/java/com/tongran/agentserver/scheduler/HeartScheduler.java new file mode 100644 index 0000000..a84ed50 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/scheduler/HeartScheduler.java @@ -0,0 +1,79 @@ +package com.tongran.agentserver.scheduler; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.tongran.agentserver.server.netty.NettyTcpClient; +import com.tongran.agentserver.server.netty.model.Message; +import com.tongran.agentserver.utils.AgentUtil; +import com.tongran.agentserver.utils.AssertLog; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.concurrent.ScheduledFuture; + +@Component +@EnableScheduling +public class HeartScheduler { + + private ScheduledFuture scheduledTask; + + @Resource + private TaskScheduler taskScheduler; + + @Resource + private NettyTcpClient nettyTcpClient; + + + @PostConstruct + public void init() { + // 等待5秒 + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + startTask(30000); // 默认30秒间隔启动 + } + + public void startTask(long intervalMillis) { + if (scheduledTask != null) { + scheduledTask.cancel(false); + } + + Runnable task = () -> { + // 业务逻辑 + if (!nettyTcpClient.isConnected()) { + try { + nettyTcpClient.connect(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + // 发送心跳包 + JSONObject object = new JSONObject(); + object.put("strength","31"); + String clientId = AgentUtil.getMotherboardUUID(); + Message message = Message.builder().clientId(clientId).dataType("HEARTBEAT").data(object.toString()).build(); + // 将对象转为 JSON 字符串 标识 + String json = "agent-tcp:"+JSON.toJSONString(message)+"@tong-ran"; + AssertLog.info("发送心跳包={}",json); + nettyTcpClient.sendMessage(json); + }; + + scheduledTask = taskScheduler.scheduleAtFixedRate(task, intervalMillis); + } + + public void updateInterval(long newIntervalMillis) { + startTask(newIntervalMillis); + } + + public void stopTask() { + if (scheduledTask != null) { + scheduledTask.cancel(false); + scheduledTask = null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/scheduler/MemoryScheduler.java b/src/main/java/com/tongran/agentserver/scheduler/MemoryScheduler.java new file mode 100644 index 0000000..40e15c4 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/scheduler/MemoryScheduler.java @@ -0,0 +1,79 @@ +package com.tongran.agentserver.scheduler; + +import com.alibaba.fastjson2.JSON; +import com.tongran.agentserver.server.collect.memory.MemoryService; +import com.tongran.agentserver.server.collect.vo.MemoryVO; +import com.tongran.agentserver.server.netty.NettyTcpClient; +import com.tongran.agentserver.server.netty.model.Message; +import com.tongran.agentserver.utils.AgentUtil; +import com.tongran.agentserver.utils.AssertLog; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.concurrent.ScheduledFuture; + +@Component +@EnableScheduling +public class MemoryScheduler { + + @Resource + private TaskScheduler taskScheduler; + + private ScheduledFuture scheduledTask; + + @Resource + private NettyTcpClient nettyTcpClient; + + @Resource + private MemoryService memoryService; + + @PostConstruct + public void init() { + // 等待47秒 + try { + Thread.sleep(47000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + startTask(300000); // 默认300秒间隔启动 + } + + public void startTask(long intervalMillis) { + if (scheduledTask != null) { + scheduledTask.cancel(false); + } + + Runnable task = () -> { + AssertLog.info("内存信息包准备={}", LocalDateTime.now()); + // 业务逻辑 + if (nettyTcpClient.isConnected()) { + // 发送内存信息包 + MemoryVO memoryVO = memoryService.query(); + String data = JSON.toJSONString(memoryVO); + String clientId = AgentUtil.getMotherboardUUID(); + Message message = Message.builder().clientId(clientId).dataType("MEMORY").data(data).build(); + // 将对象转为 JSON 字符串 + String json = "agent-tcp:"+JSON.toJSONString(message)+"@tong-ran"; + AssertLog.info("发送内存信息包={}",json); + nettyTcpClient.sendMessage(json); + } + }; + + scheduledTask = taskScheduler.scheduleAtFixedRate(task, intervalMillis); + } + + public void updateInterval(long newIntervalMillis) { + startTask(newIntervalMillis); + } + + public void stopTask() { + if (scheduledTask != null) { + scheduledTask.cancel(false); + scheduledTask = null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/scheduler/NetScheduler.java b/src/main/java/com/tongran/agentserver/scheduler/NetScheduler.java new file mode 100644 index 0000000..6e55fb9 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/scheduler/NetScheduler.java @@ -0,0 +1,81 @@ +package com.tongran.agentserver.scheduler; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.tongran.agentserver.server.collect.net.NetService; +import com.tongran.agentserver.server.collect.vo.NetVO; +import com.tongran.agentserver.server.netty.NettyTcpClient; +import com.tongran.agentserver.server.netty.model.Message; +import com.tongran.agentserver.utils.AgentUtil; +import com.tongran.agentserver.utils.AssertLog; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; +import java.util.concurrent.ScheduledFuture; + +@Component +@EnableScheduling +public class NetScheduler { + + @Resource + private TaskScheduler taskScheduler; + + private ScheduledFuture scheduledTask; + + @Resource + private NettyTcpClient nettyTcpClient; + + @Resource + private NetService netService; + + @PostConstruct + public void init() { + // 等待72秒 + try { + Thread.sleep(72000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + startTask(180000); // 默认180秒间隔启动 + } + + public void startTask(long intervalMillis) { + if (scheduledTask != null) { + scheduledTask.cancel(false); + } + + Runnable task = () -> { + AssertLog.info("网卡信息包准备={}", LocalDateTime.now()); + // 业务逻辑 + if (nettyTcpClient.isConnected()) { + // 发送网卡信息包 + List list = netService.query(); + String data = JSONArray.toJSONString(list); + String clientId = AgentUtil.getMotherboardUUID(); + Message message = Message.builder().clientId(clientId).dataType("NET").data(data).build(); + // 将对象转为 JSON 字符串 + String json = "agent-tcp:"+JSON.toJSONString(message)+"@tong-ran"; + AssertLog.info("发送网卡信息包={}",json); + nettyTcpClient.sendMessage(json); + } + }; + + scheduledTask = taskScheduler.scheduleAtFixedRate(task, intervalMillis); + } + + public void updateInterval(long newIntervalMillis) { + startTask(newIntervalMillis); + } + + public void stopTask() { + if (scheduledTask != null) { + scheduledTask.cancel(false); + scheduledTask = null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/scheduler/PointScheduler.java b/src/main/java/com/tongran/agentserver/scheduler/PointScheduler.java new file mode 100644 index 0000000..66a284a --- /dev/null +++ b/src/main/java/com/tongran/agentserver/scheduler/PointScheduler.java @@ -0,0 +1,81 @@ +package com.tongran.agentserver.scheduler; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.tongran.agentserver.server.collect.disk.DiskService; +import com.tongran.agentserver.server.collect.vo.PointVO; +import com.tongran.agentserver.server.netty.NettyTcpClient; +import com.tongran.agentserver.server.netty.model.Message; +import com.tongran.agentserver.utils.AgentUtil; +import com.tongran.agentserver.utils.AssertLog; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; +import java.util.concurrent.ScheduledFuture; + +@Component +@EnableScheduling +public class PointScheduler { + + @Resource + private TaskScheduler taskScheduler; + + private ScheduledFuture scheduledTask; + + @Resource + private NettyTcpClient nettyTcpClient; + + @Resource + private DiskService diskService; + + @PostConstruct + public void init() { + // 等待97秒 + try { + Thread.sleep(97000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + startTask(300000); // 默认300秒间隔启动 + } + + public void startTask(long intervalMillis) { + if (scheduledTask != null) { + scheduledTask.cancel(false); + } + + Runnable task = () -> { + AssertLog.info("挂载点信息包准备={}", LocalDateTime.now()); + // 业务逻辑 + if (nettyTcpClient.isConnected()) { + // 发送挂载点信息包 + List list = diskService.queryPointList(); + String data = JSONArray.toJSONString(list); + String clientId = AgentUtil.getMotherboardUUID(); + Message message = Message.builder().clientId(clientId).dataType("POINT").data(data).build(); + // 将对象转为 JSON 字符串 + String json = "agent-tcp:"+JSON.toJSONString(message)+"@tong-ran"; + AssertLog.info("发送挂载点信息包={}",json); + nettyTcpClient.sendMessage(json); + } + }; + + scheduledTask = taskScheduler.scheduleAtFixedRate(task, intervalMillis); + } + + public void updateInterval(long newIntervalMillis) { + startTask(newIntervalMillis); + } + + public void stopTask() { + if (scheduledTask != null) { + scheduledTask.cancel(false); + scheduledTask = null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/scheduler/SwitchBoardScheduler.java b/src/main/java/com/tongran/agentserver/scheduler/SwitchBoardScheduler.java new file mode 100644 index 0000000..ae90c88 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/scheduler/SwitchBoardScheduler.java @@ -0,0 +1,80 @@ +package com.tongran.agentserver.scheduler; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.tongran.agentserver.server.collect.switchboard.SwitchBoardService; +import com.tongran.agentserver.server.collect.vo.SwitchBoardVO; +import com.tongran.agentserver.server.netty.NettyTcpClient; +import com.tongran.agentserver.server.netty.model.Message; +import com.tongran.agentserver.utils.AgentUtil; +import com.tongran.agentserver.utils.AssertLog; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; +import java.util.concurrent.ScheduledFuture; + +@Component +@EnableScheduling +public class SwitchBoardScheduler { + @Resource + private TaskScheduler taskScheduler; + + private ScheduledFuture scheduledTask; + + @Resource + private NettyTcpClient nettyTcpClient; + + @Resource + private SwitchBoardService switchBoardService; + + @PostConstruct + public void init() { + // 等待12秒 + try { + Thread.sleep(12000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + startTask(80000); // 默认3分钟间隔启动 + } + + public void startTask(long intervalMillis) { + if (scheduledTask != null) { + scheduledTask.cancel(false); + } + Runnable task = () -> { + AssertLog.info("交换机信息包准备={}", LocalDateTime.now()); + // 业务逻辑 + if (nettyTcpClient.isConnected()) { + // 发送交换机信息包 + List list = switchBoardService.query(); + String data = JSONArray.toJSONString(list); + String clientId = AgentUtil.getMotherboardUUID(); + Message message = Message.builder().clientId(clientId).dataType("SWITCHBOARD").data(data).build(); + // 将对象转为 JSON 字符串 + String json = "agent-tcp:"+ JSON.toJSONString(message)+"@tong-ran"; + AssertLog.info("发送交换机信息包={}",json); + nettyTcpClient.sendMessage(json); + } + }; + + scheduledTask = taskScheduler.scheduleAtFixedRate(task, intervalMillis); + } + + public void updateInterval(long newIntervalMillis) { + startTask(newIntervalMillis); + } + + public void stopTask() { + if (scheduledTask != null) { + scheduledTask.cancel(false); + scheduledTask = null; + } + } + +} diff --git a/src/main/java/com/tongran/agentserver/scheduler/SysScheduler.java b/src/main/java/com/tongran/agentserver/scheduler/SysScheduler.java new file mode 100644 index 0000000..c609525 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/scheduler/SysScheduler.java @@ -0,0 +1,78 @@ +package com.tongran.agentserver.scheduler; + +import com.alibaba.fastjson2.JSON; +import com.tongran.agentserver.server.collect.system.SystemService; +import com.tongran.agentserver.server.collect.vo.SystemVO; +import com.tongran.agentserver.server.netty.NettyTcpClient; +import com.tongran.agentserver.server.netty.model.Message; +import com.tongran.agentserver.utils.AgentUtil; +import com.tongran.agentserver.utils.AssertLog; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.concurrent.ScheduledFuture; + +@Component +@EnableScheduling +public class SysScheduler { + + @Resource + private TaskScheduler taskScheduler; + + private ScheduledFuture scheduledTask; + + @Resource + private NettyTcpClient nettyTcpClient; + + @Resource + private SystemService systemService; + + @PostConstruct + public void init() { + // 等待50秒 + try { + Thread.sleep(50000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + startTask(300000); // 默认300秒间隔启动 + } + + public void startTask(long intervalMillis) { + if (scheduledTask != null) { + scheduledTask.cancel(false); + } + Runnable task = () -> { + AssertLog.info("系统信息包准备={}", LocalDateTime.now()); + // 业务逻辑 + if (nettyTcpClient.isConnected()) { + SystemVO systemVO = systemService.query(); + // 发送系统信息包 + String data = JSON.toJSONString(systemVO); + String clientId = AgentUtil.getMotherboardUUID(); + Message message = Message.builder().clientId(clientId).dataType("SYSTEM").data(data).build(); + // 将对象转为 JSON 字符串 + String json = "agent-tcp:"+JSON.toJSONString(message)+"@tong-ran"; + AssertLog.info("发送系统信息包={}",json); + nettyTcpClient.sendMessage(json); + } + }; + + scheduledTask = taskScheduler.scheduleAtFixedRate(task, intervalMillis); + } + + public void updateInterval(long newIntervalMillis) { + startTask(newIntervalMillis); + } + + public void stopTask() { + if (scheduledTask != null) { + scheduledTask.cancel(false); + scheduledTask = null; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/server/collect/cpu/CpuService.java b/src/main/java/com/tongran/agentserver/server/collect/cpu/CpuService.java new file mode 100644 index 0000000..7d895d5 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/cpu/CpuService.java @@ -0,0 +1,7 @@ +package com.tongran.agentserver.server.collect.cpu; + +import com.tongran.agentserver.server.collect.vo.CpuVO; + +public interface CpuService { + CpuVO query(); +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/cpu/impl/CpuServiceImpl.java b/src/main/java/com/tongran/agentserver/server/collect/cpu/impl/CpuServiceImpl.java new file mode 100644 index 0000000..bcca1a0 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/cpu/impl/CpuServiceImpl.java @@ -0,0 +1,165 @@ +package com.tongran.agentserver.server.collect.cpu.impl; + +import com.tongran.agentserver.server.collect.cpu.CpuService; +import com.tongran.agentserver.server.collect.vo.CpuVO; +import org.springframework.stereotype.Service; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.OperatingSystem; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@Service +public class CpuServiceImpl implements CpuService { + + @Override + public CpuVO query() { + CpuVO cpuVO = CpuVO.builder().build(); + try { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + CentralProcessor processor = hal.getProcessor(); + OperatingSystem os = si.getOperatingSystem(); + + System.out.println("========================================================="); + // 1. CPU基本信息 + cpuVO.setNum(processor.getPhysicalProcessorCount()); // CUP数量 + System.out.println("=== CPU基本信息 ==="); + System.out.println("CPU型号: " + processor.getProcessorIdentifier().getName()); + System.out.println("物理核心数: " + processor.getPhysicalProcessorCount()); + System.out.println("逻辑核心数: " + processor.getLogicalProcessorCount()); + System.out.println("最大频率: " + processor.getMaxFreq() / 1_000_000.0 + " GHz"); + + // 2. CPU负载信息 + System.out.println("\n=== CPU负载信息 ==="); + double[] loadAverage = processor.getSystemLoadAverage(3); + cpuVO.setAvg1(loadAverage[0]);// CUP1分钟负载 + cpuVO.setAvg1(loadAverage[1]);// CUP5分钟负载 + cpuVO.setAvg1(loadAverage[2]);// CUP15分钟负载 + System.out.println("1分钟平均负载: " + loadAverage[0]); + System.out.println("5分钟平均负载: " + loadAverage[1]); + System.out.println("15分钟平均负载: " + loadAverage[2]); + + + // 3. CPU使用率(需要两次采样) + System.out.println("\n=== CPU使用率 ==="); + long[] prevTicks = processor.getSystemCpuLoadTicks(); + TimeUnit.SECONDS.sleep(1); // 等待1秒 + // 计算使用率 + double cpuUsage = processor.getSystemCpuLoadBetweenTicks(prevTicks); + cpuVO.setUti(cpuUsage * 100); // CPU使用率 + System.out.printf("CPU 使用率: %.2f%%\n", cpuUsage * 100); + long[] ticks = processor.getSystemCpuLoadTicks(); + long user = ticks[CentralProcessor.TickType.USER.getIndex()] - + prevTicks[CentralProcessor.TickType.USER.getIndex()]; + long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - + prevTicks[CentralProcessor.TickType.NICE.getIndex()]; + long sys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - + prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()]; + long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - + prevTicks[CentralProcessor.TickType.IDLE.getIndex()]; + long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - + prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()]; + long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - + prevTicks[CentralProcessor.TickType.IRQ.getIndex()]; + long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - + prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]; + long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - + prevTicks[CentralProcessor.TickType.STEAL.getIndex()]; + long total = user + nice + sys + idle + iowait + irq + softirq + steal; + +// System.out.printf("用户进程时间: %.2f%%\n", 100d * user / total); +// System.out.printf("低优先级进程时间: %.2f%%\n", 100d * nice / total); +// System.out.printf("系统时间: %.2f%%\n", 100d * sys / total); +// System.out.printf("空闲时间: %.2f%%\n", 100d * idle / total); +// System.out.printf("I/O等待时间: %.2f%%\n", 100d * iowait / total); +// System.out.printf("硬件中断时间: %.2f%%\n", 100d * irq / total); +// System.out.printf("软中断时间: %.2f%%\n", 100d * softirq / total); +// System.out.printf("虚拟化等待时间: %.2f%%\n", 100d * steal / total); + // 4. CPU时间累计值 + System.out.println("\n=== CPU时间累计值 ==="); + long[] allTicks = processor.getSystemCpuLoadTicks(); + System.out.println("中断累计时间: " + + (allTicks[CentralProcessor.TickType.IRQ.getIndex()] + + allTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()])); + System.out.println("空闲累计时间: " + allTicks[CentralProcessor.TickType.IDLE.getIndex()]); + System.out.printf("I/O等待时间(CPU等待响应时间): %.2f%%\n", 100d * iowait / total); + System.out.println("系统累计时间: " + allTicks[CentralProcessor.TickType.SYSTEM.getIndex()]); + long softwareTime = sys - irq - softirq; + System.out.printf("软件相关时间(近似无响应时间): %.2f%%\n", 100d * softwareTime / total); + System.out.println("用户进程累计时间: " + allTicks[CentralProcessor.TickType.USER.getIndex()]); + + + cpuVO.setInterrupt((allTicks[CentralProcessor.TickType.IRQ.getIndex()] + + allTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()])); // CPU硬件中断提供服务时间 + cpuVO.setIdle(allTicks[CentralProcessor.TickType.IDLE.getIndex()]);// CPU空闲时间 + cpuVO.setIowait(100d * iowait / total);// CPU等待响应时间 + cpuVO.setSystem(allTicks[CentralProcessor.TickType.SYSTEM.getIndex()]);// CPU系统时间 + cpuVO.setNoresp(100d * softwareTime / total);// CPU软件无响应时间 + cpuVO.setUser(allTicks[CentralProcessor.TickType.USER.getIndex()]);// CPU用户进程所花费的时间 + +// System.out.println("用户进程累计时间: " + allTicks[CentralProcessor.TickType.USER.getIndex()]); +// System.out.println("系统累计时间: " + allTicks[CentralProcessor.TickType.SYSTEM.getIndex()]); +// System.out.println("空闲累计时间: " + allTicks[CentralProcessor.TickType.IDLE.getIndex()]); +// System.out.println("中断累计时间: " + +// (allTicks[CentralProcessor.TickType.IRQ.getIndex()] + +// allTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()])); +// System.out.printf("I/O等待时间(CPU等待响应时间): %.2f%%\n", 100d * iowait / total); +// System.out.printf("软件相关时间(近似无响应时间): %.2f%%\n", 100d * softwareTime / total); + + // 5. 系统运行时间和CPU空闲时间 +// System.out.println("\n=== 系统运行时间 ==="); + long uptime = os.getSystemUptime(); + cpuVO.setNormal(uptime);// CPU正常运行时间 + System.out.println("CPU正常运行时间: " + cpuVO.getNormal()); +// Duration duration = Duration.ofSeconds(uptime); +// System.out.printf("系统已运行: %d天 %d小时 %d分钟 %d秒\n", +// duration.toDays(), +// duration.toHours() % 24, +// duration.toMinutes() % 60, +// duration.getSeconds() % 60); + return cpuVO; + } catch (InterruptedException e) { + e.printStackTrace(); + } + return null; + } + + public static double getCpuUsage() { + try { + SystemInfo si = new SystemInfo(); + CentralProcessor processor = si.getHardware().getProcessor(); + + // 第一次调用获取初始值 + long[] prevTicks = processor.getSystemCpuLoadTicks(); + // 等待1秒 + Thread.sleep(1000); + // 计算使用率 + double cpuUsage = processor.getSystemCpuLoadBetweenTicks(prevTicks); + System.out.printf("CPU 使用率: %.2f%%\n", cpuUsage * 100); + return cpuUsage; + + } catch (InterruptedException e) { + e.printStackTrace(); + } + return 0; + } + + public static String getMachineId() { + try { + // 尝试读取Linux系统的machine-id文件 + String machineId = new String(Files.readAllBytes(Paths.get("/etc/machine-id"))).trim(); + if (machineId.isEmpty()) { + machineId = new String(Files.readAllBytes(Paths.get("/var/lib/dbus/machine-id"))).trim(); + } + return machineId; + } catch (IOException e) { + return UUID.randomUUID().toString(); + } + } +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/disk/DiskService.java b/src/main/java/com/tongran/agentserver/server/collect/disk/DiskService.java new file mode 100644 index 0000000..26658b6 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/disk/DiskService.java @@ -0,0 +1,12 @@ +package com.tongran.agentserver.server.collect.disk; + +import com.tongran.agentserver.server.collect.vo.DiskVO; +import com.tongran.agentserver.server.collect.vo.PointVO; + +import java.util.List; + +public interface DiskService { + List queryDiskList(); + + List queryPointList(); +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/disk/impl/DiskServiceImpl.java b/src/main/java/com/tongran/agentserver/server/collect/disk/impl/DiskServiceImpl.java new file mode 100644 index 0000000..e92e368 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/disk/impl/DiskServiceImpl.java @@ -0,0 +1,132 @@ +package com.tongran.agentserver.server.collect.disk.impl; + +import com.tongran.agentserver.server.collect.disk.DiskService; +import com.tongran.agentserver.server.collect.vo.DiskVO; +import com.tongran.agentserver.server.collect.vo.PointVO; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import oshi.SystemInfo; +import oshi.hardware.HWDiskStore; +import oshi.software.os.OSFileStore; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Service +public class DiskServiceImpl implements DiskService { + @Override + public List queryDiskList() { + List tempList = new ArrayList<>(); + List resultList = new ArrayList<>(); + SystemInfo si = new SystemInfo(); + + System.out.println("========================================================="); + // 获取磁盘IO信息 + System.out.println("\n=== 磁盘IO信息 ==="); + for (HWDiskStore disk : si.getHardware().getDiskStores()) { + DiskVO diskVO = DiskVO.builder().build(); + diskVO.setName(disk.getName());//磁盘名称 + diskVO.setSerial(disk.getSerial());//序列号 + diskVO.setTotal(disk.getSize());//磁盘大小(GB) + diskVO.setWriteTimes(disk.getWrites());//磁盘写入次数 + diskVO.setReadTimes(disk.getReads());//磁盘读取次数 + diskVO.setWriteBytes(disk.getReadBytes());//磁盘写入字节 + diskVO.setReadBytes(disk.getWriteBytes());//磁盘读取字节 + tempList.add(diskVO); + + System.out.println("磁盘名称: " + diskVO.getName()); + System.out.println("序列号: " + diskVO.getSerial()); + System.out.println("磁盘大小: " + diskVO.getTotal()); + System.out.println("磁盘写入次数: " + diskVO.getWriteTimes()); + System.out.println("磁盘读取次数: " + diskVO.getReadTimes()); + System.out.println("磁盘写入字节: " + diskVO.getWriteBytes()); + System.out.println("磁盘读取字节: " + diskVO.getReadBytes()); + + // 分区信息 +// for (HWPartition part : disk.getPartitions()) { +// System.out.println(" 分区: " + part.getIdentification() + +// " 大小: " + part.getSize() / (1024 * 1024 * 1024) + " GB"); +// } +// System.out.println("读取次数: " + disk.getReads()); +// System.out.println("写入次数: " + disk.getWrites()); +// System.out.println("读取字节: " + disk.getReadBytes() / (1024 * 1024) + " MB"); +// System.out.println("写入字节: " + disk.getWriteBytes() / (1024 * 1024) + " MB"); +// System.out.println("------------------------"); + } + + try{ + // 第一次采样 + List disks1 = si.getHardware().getDiskStores(); + long[] readBytes1 = new long[disks1.size()]; + long[] writeBytes1 = new long[disks1.size()]; + for (int i = 0; i < disks1.size(); i++) { + readBytes1[i] = disks1.get(i).getReadBytes(); + writeBytes1[i] = disks1.get(i).getWriteBytes(); + } + // 等待1秒 + Thread.sleep(1000); + // 第二次采样 + List disks2 = si.getHardware().getDiskStores(); + for (int i = 0; i < disks2.size(); i++) { + long readDiff = disks2.get(i).getReadBytes() - readBytes1[i]; + long writeDiff = disks2.get(i).getWriteBytes() - writeBytes1[i]; + String serial = disks2.get(i).getSerial(); + DiskVO diskVO = tempList.stream().filter(d -> StringUtils.equals(d.getSerial(),serial)).findFirst().orElse(null); + if(Objects.nonNull(diskVO)){ + diskVO.setWriteSpeed(readDiff);//磁盘写入速率 + diskVO.setReadSpeed(writeDiff);//磁盘读取速率 + System.out.println("磁盘名称: " + diskVO.getName()); + System.out.println("磁盘写入速率: " + diskVO.getWriteSpeed()); + System.out.println("磁盘读取速率: " + diskVO.getReadSpeed()); + } + resultList.add(diskVO); +// System.out.printf("%s: 读 %d KB/s, 写 %d KB/s%n", +// disks2.get(i).getName(), +// readDiff / 1024, +// writeDiff / 1024); + } + } catch (Exception e) { + e.printStackTrace(); + } + return resultList; + } + + @Override + public List queryPointList() { + List list = new ArrayList<>(); + SystemInfo si = new SystemInfo(); + // 获取文件系统信息 + System.out.println("========================================================="); + System.out.println("=== 挂载信息 ==="); + for (OSFileStore fs : si.getOperatingSystem().getFileSystem().getFileStores()) { + long totalSpace = fs.getTotalSpace(); + long usableSpace = fs.getUsableSpace(); + long freeSpace = fs.getFreeSpace(); + double usagePercentage = totalSpace > 0 ? + (double) (totalSpace - freeSpace) / totalSpace * 100 : 0; + + PointVO pointVO = PointVO.builder().build(); + pointVO.setMount(fs.getMount());//挂载点 + pointVO.setVfsType(fs.getType());//文件系统类型 + pointVO.setVfsTotal(totalSpace);//总空间 + pointVO.setVfsFree(usableSpace);//可用空间 + pointVO.setVfsUtil(usagePercentage);//空间利用率 + list.add(pointVO); + System.out.println("挂载点: " + pointVO.getMount()); + System.out.println("文件系统类型: " + pointVO.getVfsType()); + System.out.println("总空间: " + pointVO.getVfsTotal()); + System.out.println("可用空间: " + pointVO.getVfsFree()); + System.out.printf("空间利用率: %.2f%%\n", usagePercentage); + +// System.out.println("挂载点: " + fs.getMount()); +// System.out.println("总空间: " + fs.getTotalSpace() / (1024 * 1024 * 1024) + " GB"); +// System.out.println("可用空间: " + fs.getFreeSpace() / (1024 * 1024 * 1024) + " GB"); +// System.out.println("使用率: " + String.format("%.1f", +// 100.0 * (fs.getTotalSpace() - fs.getFreeSpace()) / fs.getTotalSpace()) + "%"); +// System.out.println("文件系统类型: " + fs.getType()); +// System.out.println("------------------------"); + } + return list; + } +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/docker/DockerService.java b/src/main/java/com/tongran/agentserver/server/collect/docker/DockerService.java new file mode 100644 index 0000000..bd29941 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/docker/DockerService.java @@ -0,0 +1,9 @@ +package com.tongran.agentserver.server.collect.docker; + +import com.tongran.agentserver.server.collect.vo.DockerVO; + +import java.util.List; + +public interface DockerService { + List query(); +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/docker/impl/DockerServiceImpl.java b/src/main/java/com/tongran/agentserver/server/collect/docker/impl/DockerServiceImpl.java new file mode 100644 index 0000000..f69f9a6 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/docker/impl/DockerServiceImpl.java @@ -0,0 +1,114 @@ +package com.tongran.agentserver.server.collect.docker.impl; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.Container; +import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.DockerClientBuilder; +import com.tongran.agentserver.server.collect.docker.DockerService; +import com.tongran.agentserver.server.collect.vo.DockerVO; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +@Service +public class DockerServiceImpl implements DockerService { + @Override + public List query(){ + List list = new ArrayList<>(); + // 配置Docker客户端 + DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder() + .withDockerHost("unix:///var/run/docker.sock") // 本地Docker + // 或远程Docker: .withDockerHost("tcp://your-docker-host:2375") + .build(); + DockerClient dockerClient = DockerClientBuilder.getInstance(config).build(); + + try { + // 获取正在运行的容器列表 + List containers = dockerClient.listContainersCmd() + .withShowAll(false) // 只显示运行中的容器 + .exec(); + + // 打印容器信息 + System.out.println("运行中的Docker容器:"); + System.out.println("容器ID\t\t镜像\t\t状态\t\t名称"); + for (Container container : containers) { + DockerVO dockerVO = DockerVO.builder().build(); + String id = container.getId().substring(0, 12); // 只显示短ID + String image = container.getImage().length() > 15 ? + container.getImage().substring(0, 15) + "..." : container.getImage(); + String status = container.getStatus(); + String name = container.getNames()[0].replaceFirst("/", ""); + System.out.printf("%s\t%s\t%s\t%s%n", id, image, status, name); + dockerVO.setId(id); + dockerVO.setName(name); + dockerVO.setStatus(status); + DockerVO res = dockerStats(dockerVO); + list.add(res); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + dockerClient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return list; + } + + public DockerVO dockerStats(DockerVO dockerVO) { + //判定目标容器 ID + if(StringUtils.isBlank(dockerVO.getId())){ + return dockerVO; + } + try { + // 执行 docker stats 命令(--no-stream 表示只输出一次) + Process process = new ProcessBuilder( + "docker", "stats", "--no-stream", dockerVO.getId() + ).start(); + + // 读取命令输出 + BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()) + ); + + String line; + while ((line = reader.readLine()) != null) { + // 解析输出(示例输出格式): + // "your_container_id 0.00% 0.000 CPU % 0 B / 0 B 0 packets / 0 packets" + // 实际输出可能因 Docker 版本不同而变化,需根据实际情况调整正则表达式 + if (line.contains(dockerVO.getId())) { + String[] parts = line.trim().split("\\s+"); + String cpuUtil = parts[2]; // cpu使用率 + String memUtil = parts[6]; // 内存使用率 + // 网络流量通常在第 4 列(接收)和第 5 列(发送),格式为 "B/s" 或 "B" + String rxRate = parts[7]; // 接收速率(如 "1.23kB/s") + String txRate = parts[9]; // 发送速率(如 "4.56kB/s") + dockerVO.setCpuUtil(cpuUtil); + dockerVO.setMemUtil(memUtil); + dockerVO.setNetInSpeed(rxRate); + dockerVO.setNetOutSpeed(txRate); + + System.out.println("==================== 容器网络流量 ===================="); + System.out.println("接收速率: " + rxRate); + System.out.println("发送速率: " + txRate); + } + } + // 等待命令执行完成并获取退出码 + int exitCode = process.waitFor(); + if (exitCode != 0) { + System.err.println("命令执行失败,退出码: " + exitCode); + } + reader.close(); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + return dockerVO; + } +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/memory/MemoryService.java b/src/main/java/com/tongran/agentserver/server/collect/memory/MemoryService.java new file mode 100644 index 0000000..c3b2749 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/memory/MemoryService.java @@ -0,0 +1,7 @@ +package com.tongran.agentserver.server.collect.memory; + +import com.tongran.agentserver.server.collect.vo.MemoryVO; + +public interface MemoryService { + MemoryVO query(); +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/memory/impl/MemoryServiceImpl.java b/src/main/java/com/tongran/agentserver/server/collect/memory/impl/MemoryServiceImpl.java new file mode 100644 index 0000000..20cf516 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/memory/impl/MemoryServiceImpl.java @@ -0,0 +1,228 @@ +package com.tongran.agentserver.server.collect.memory.impl; + +import com.tongran.agentserver.server.collect.memory.MemoryService; +import com.tongran.agentserver.server.collect.vo.MemoryVO; +import org.springframework.stereotype.Service; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@Service +public class MemoryServiceImpl implements MemoryService { + @Override + public MemoryVO query() { + MemoryVO memoryVO = MemoryVO.builder().build(); + try { + Map memInfo = parseMemInfo(); + + System.out.println("========================================================="); + // 1. 基本内存信息 + System.out.println("=== 基本内存信息 (单位KB) ==="); + + System.out.println("总内存: " + memInfo.get("MemTotal")); + System.out.println("空闲内存: " + memInfo.get("MemFree")); + System.out.println("可用内存: " + + memInfo.getOrDefault("MemAvailable", + memInfo.get("MemFree") + + memInfo.getOrDefault("Buffers", 0L) + + memInfo.getOrDefault("Cached", 0L) + + memInfo.getOrDefault("SReclaimable", 0L))); + memoryVO.setAvailable(memInfo.getOrDefault("MemAvailable", + memInfo.get("MemFree") + + memInfo.getOrDefault("Buffers", 0L) + + memInfo.getOrDefault("Cached", 0L) + + memInfo.getOrDefault("SReclaimable", 0L))); //可用内存 + memoryVO.setTotal(memInfo.get("MemTotal")); //总内存 + memoryVO.setPercent((double) memoryVO.getAvailable() / memoryVO.getTotal() * 100); //可用内存百分比 + +// System.out.println("可用内存: " + memoryVO.getAvailable()); +// System.out.println("总内存: " + memoryVO.getTotal()); +// System.out.println("可用内存百分比: " + memoryVO.getPercent()); + + + // 3. 交换空间信息 + System.out.println("\n=== 交换空间信息 ==="); + long swapTotal = memInfo.getOrDefault("SwapTotal", 0L); + long swapFree = memInfo.getOrDefault("SwapFree", 0L); + System.out.println("总交换空间: " + swapTotal); + System.out.println("空闲交换空间: " + swapFree); + System.out.println("已用交换空间: " + (swapTotal - swapFree)); + + memoryVO.setSwapSizeFree(swapFree); //交换卷/文件的可用空间(字节) + memoryVO.setSwapSizePercent((double) swapFree / swapTotal * 100); //可用交换空间百分比 + +// System.out.println("交换卷/文件的可用空间(字节): " + memoryVO.getSwapSizeFree()); +// System.out.println("可用交换空间百分比: " + memoryVO.getSwapSizePercent()); + +// System.out.println("总交换空间: " + swapTotal); +// System.out.println("空闲交换空间: " + swapFree); +// System.out.println("已用交换空间: " + (swapTotal - swapFree)); + if (swapTotal > 0) { + System.out.printf("交换空间使用率: %.2f%%\n", + (double)(swapTotal - swapFree) / swapTotal * 100); + + } + + // 4. 内存使用率分析 + System.out.println("\n=== 内存使用率分析 ==="); + long total = memInfo.get("MemTotal"); + long available = memInfo.getOrDefault("MemAvailable", + memInfo.get("MemFree") + + memInfo.getOrDefault("Buffers", 0L) + + memInfo.getOrDefault("Cached", 0L) + + memInfo.getOrDefault("SReclaimable", 0L)); + // 总内存使用率 + double totalUsage = (double)(total - available) / total * 100; + System.out.printf("总内存使用率: %.2f%%\n", totalUsage); + // 实际内存使用率 + long cached = memInfo.getOrDefault("Cached", 0L) + + memInfo.getOrDefault("SReclaimable", 0L); + long buffers = memInfo.getOrDefault("Buffers", 0L); + double actualUsage = (double)(total - available - cached - buffers) / total * 100; + System.out.printf("实际内存使用率: %.2f%%\n", actualUsage); + memoryVO.setUntilzation(actualUsage); //内存利用率 + +// System.out.println("内存利用率: " + memoryVO.getUntilzation()); + +// System.out.printf("实际内存使用率: %.2f%%\n", actualUsage); + // 内存压力分析 +// if (memInfo.containsKey("MemAvailable")) { +// long memAvailable = memInfo.get("MemAvailable"); +// double availableRatio = (double)memAvailable / total * 100; +// System.out.printf("可用内存占比: %.2f%%\n", availableRatio); +// +// if (availableRatio < 10) { +// System.out.println("警告: 可用内存不足,系统可能会开始使用交换空间"); +// } +// } + + return memoryVO; + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + + public static Map parseMemInfo() throws IOException { + Map memInfo = new HashMap<>(); + try (BufferedReader br = new BufferedReader(new FileReader("/proc/meminfo"))) { + String line; + while ((line = br.readLine()) != null) { + String[] parts = line.split("\\s+"); + if (parts.length >= 2) { + String key = parts[0].replace(":", ""); + long value = Long.parseLong(parts[1]); + memInfo.put(key, value); + } + } + } + return memInfo; + } + + public static String printBasicMemoryInfo(Map memInfo) { + String result = ""; + System.out.println("=== 基本内存信息 (单位KB) ==="); + System.out.println("总内存: " + memInfo.get("MemTotal")); + System.out.println("空闲内存: " + memInfo.get("MemFree")); + System.out.println("可用内存: " + + memInfo.getOrDefault("MemAvailable", + memInfo.get("MemFree") + + memInfo.getOrDefault("Buffers", 0L) + + memInfo.getOrDefault("Cached", 0L) + + memInfo.getOrDefault("SReclaimable", 0L))); + result += "=== 基本内存信息 (单位KB) ==="; + result += "\n总内存:" + memInfo.get("MemTotal"); + result += "\n空闲内存: " + memInfo.get("MemFree"); + result += "\n可用内存: " + + memInfo.getOrDefault("MemAvailable", + memInfo.get("MemFree") + + memInfo.getOrDefault("Buffers", 0L) + + memInfo.getOrDefault("Cached", 0L) + + memInfo.getOrDefault("SReclaimable", 0L)); + return result; + } + + public static String printDetailedMemoryInfo(Map memInfo) { + String result = ""; + System.out.println("\n=== 详细内存信息 ==="); + System.out.println("缓冲区: " + memInfo.getOrDefault("Buffers", 0L)); + System.out.println("缓存: " + + (memInfo.getOrDefault("Cached", 0L) + + memInfo.getOrDefault("SReclaimable", 0L))); + System.out.println("共享内存: " + memInfo.getOrDefault("Shmem", 0L)); + System.out.println("Slab内存: " + memInfo.getOrDefault("Slab", 0L)); + + result += "\n=== 详细内存信息 ==="; + result += "\n缓冲区: " + memInfo.getOrDefault("Buffers", 0L); + result += "\n缓存: " + + (memInfo.getOrDefault("Cached", 0L) + + memInfo.getOrDefault("SReclaimable", 0L)); + result += "\n共享内存: " + memInfo.getOrDefault("Shmem", 0L); + result += "\nSlab内存: " + memInfo.getOrDefault("Slab", 0L); + + return result; + } + + public static String printSwapInfo(Map memInfo) { + String result = ""; + System.out.println("\n=== 交换空间信息 ==="); + long swapTotal = memInfo.getOrDefault("SwapTotal", 0L); + long swapFree = memInfo.getOrDefault("SwapFree", 0L); + + System.out.println("总交换空间: " + swapTotal); + System.out.println("空闲交换空间: " + swapFree); + System.out.println("已用交换空间: " + (swapTotal - swapFree)); + result += "\n=== 交换空间信息 ==="; + result += "\n总交换空间: " + swapTotal; + result += "\n空闲交换空间: " + swapFree; + result += "\n已用交换空间: " + (swapTotal - swapFree); + if (swapTotal > 0) { + System.out.printf("交换空间使用率: %.2f%%\n", + (double)(swapTotal - swapFree) / swapTotal * 100); + result += "交换空间使用率: %.2f%%:"+ + (double)(swapTotal - swapFree) / swapTotal * 100; + } + return result; + } + + public static String analyzeMemoryUsage(Map memInfo) { + String result = ""; + System.out.println("\n=== 内存使用率分析 ==="); + result += "\n=== 内存使用率分析 ==="; + long total = memInfo.get("MemTotal"); + long available = memInfo.getOrDefault("MemAvailable", + memInfo.get("MemFree") + + memInfo.getOrDefault("Buffers", 0L) + + memInfo.getOrDefault("Cached", 0L) + + memInfo.getOrDefault("SReclaimable", 0L)); + + // 总内存使用率 + double totalUsage = (double)(total - available) / total * 100; + System.out.printf("总内存使用率: %.2f%%\n", totalUsage); + result += "总内存使用率: %.2f%%:"+totalUsage; + // 实际内存使用率 + long cached = memInfo.getOrDefault("Cached", 0L) + + memInfo.getOrDefault("SReclaimable", 0L); + long buffers = memInfo.getOrDefault("Buffers", 0L); + double actualUsage = (double)(total - available - cached - buffers) / total * 100; + System.out.printf("实际内存使用率: %.2f%%\n", actualUsage); + result += "实际内存使用率: %.2f%%:"+ actualUsage; + // 内存压力分析 + if (memInfo.containsKey("MemAvailable")) { + long memAvailable = memInfo.get("MemAvailable"); + double availableRatio = (double)memAvailable / total * 100; + System.out.printf("可用内存占比: %.2f%%\n", availableRatio); + result += "可用内存占比: %.2f%%:"+ availableRatio; + + if (availableRatio < 10) { + System.out.println("警告: 可用内存不足,系统可能会开始使用交换空间"); + result += "警告: 可用内存不足,系统可能会开始使用交换空间"; + } + } + return result; + } +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/net/NetService.java b/src/main/java/com/tongran/agentserver/server/collect/net/NetService.java new file mode 100644 index 0000000..4417392 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/net/NetService.java @@ -0,0 +1,9 @@ +package com.tongran.agentserver.server.collect.net; + +import com.tongran.agentserver.server.collect.vo.NetVO; + +import java.util.List; + +public interface NetService { + List query(); +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/net/impl/NetServiceImpl.java b/src/main/java/com/tongran/agentserver/server/collect/net/impl/NetServiceImpl.java new file mode 100644 index 0000000..a69fcdd --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/net/impl/NetServiceImpl.java @@ -0,0 +1,160 @@ +package com.tongran.agentserver.server.collect.net.impl; + +import com.tongran.agentserver.server.collect.net.NetService; +import com.tongran.agentserver.server.collect.vo.NetVO; +import com.tongran.agentserver.utils.EscapeUtil; +import org.springframework.stereotype.Service; +import oshi.SystemInfo; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.hardware.NetworkIF; +import oshi.util.FormatUtil; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +@Service +public class NetServiceImpl implements NetService { + @Override + public List query() { + List list = new ArrayList<>(); + try { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + // 获取所有网络接口 + List networkIFs = hal.getNetworkIFs(); + System.out.println("========================================================="); + System.out.println("===== 网卡流量统计 ====="); + List temp = new ArrayList<>(); + for (NetworkIF net : networkIFs) { + System.out.println("接口名称: " + net.getName()+"("+net.getDisplayName()+")"); + System.out.println("MAC地址: " + net.getMacaddr()); + System.out.print("运行状态: "); + System.out.println(net.isConnectorPresent() ? "已连接" : "未连接"); + System.out.println("接口类型: " + EscapeUtil.getInterfaceType(net)); + System.out.println("IPv4地址: " + String.join(", ", net.getIPv4addr())); + + NetVO netVO = NetVO.builder().build(); + netVO.setName(net.getName()+"("+net.getDisplayName()+")");//网卡名称 + netVO.setMac(net.getMacaddr());//MAC + netVO.setStatus(net.isConnectorPresent() ? "已连接" : "未连接");//运行状态 + netVO.setType(EscapeUtil.getInterfaceType(net));//接口类型 + netVO.setIpV4(String.join(", ", net.getIPv4addr()));//IPv4 + temp.add(netVO); + } + // 2. 实时带宽监控(需要两次采样) + System.out.println("\n=== 实时带宽监控 ==="); + // 第一次采样 + for (NetworkIF net : networkIFs) { + net.updateAttributes(); + } + + // 等待1秒 + TimeUnit.SECONDS.sleep(1); + + // 第二次采样并计算速率 + for (NetworkIF net : networkIFs) { + long prevBytesRecv = net.getBytesRecv(); + long prevBytesSent = net.getBytesSent(); + net.updateAttributes(); + long bytesRecv = net.getBytesRecv() - prevBytesRecv; + long bytesSent = net.getBytesSent() - prevBytesSent; + System.out.println("接口: " + net.getName()); + System.out.println("入站丢包: " + net.getInDrops()); + System.out.println("出站丢包: " + net.getCollisions()); + System.out.println("接收带宽: " + FormatUtil.formatBytes(bytesRecv) + "/s (" + + bytesToMbps(bytesRecv) + " Mbps)"); + System.out.println("发送带宽: " + FormatUtil.formatBytes(bytesSent) + "/s (" + + bytesToMbps(bytesSent) + " Mbps)"); + + NetVO netVO = temp.stream().filter(n -> n.getIpV4().equals(String.join(", ", net.getIPv4addr())) + && n.getMac().equals(net.getMacaddr())).findFirst().orElse(null); + if(Objects.nonNull(netVO)){ + netVO.setInDropped(net.getInDrops());//入站丢包 + netVO.setOutDropped(net.getCollisions());//出站丢包 + netVO.setInSpeed(FormatUtil.formatBytes(bytesRecv));//接收流量 + netVO.setOutSpeed(FormatUtil.formatBytes(bytesSent));//发送流量 + list.add(netVO); + } + } + +// // 第一次获取数据 +// networkIFs.forEach(NetworkIF::updateAttributes); +// // 获取初始值 +// long[] initialRxBytes = new long[networkIFs.size()]; +// long[] initialTxBytes = new long[networkIFs.size()]; +// for (int i = 0; i < networkIFs.size(); i++) { +// initialRxBytes[i] = networkIFs.get(i).getBytesRecv(); +// initialTxBytes[i] = networkIFs.get(i).getBytesSent(); +// } +// // 等待1秒获取变化量 +// try { +// Thread.sleep(1000); +// } catch (InterruptedException e) { +// Thread.currentThread().interrupt(); +// } +// // 第二次获取数据 +// networkIFs.forEach(NetworkIF::updateAttributes); +// System.out.println("========================================================="); +// System.out.println("===== 网卡流量统计 ====="); +// for (int i = 0; i < networkIFs.size(); i++) { +// NetworkIF net = networkIFs.get(i); +// long currentRx = net.getBytesRecv(); +// long currentTx = net.getBytesSent(); +// // 计算1秒内的流量变化 +// long rxDiff = currentRx - initialRxBytes[i]; +// long txDiff = currentTx - initialTxBytes[i]; +// NetVO netVO = NetVO.builder().build(); +// netVO.setName(net.getName()+"("+net.getDisplayName()+")");//网卡名称 +// netVO.setMac(net.getMacaddr());//MAC +// netVO.setStatus(net.isConnectorPresent() ? "已连接" : "未连接");//运行状态 +// netVO.setType(EscapeUtil.getInterfaceType(net));//接口类型 +// netVO.setIpV4(String.join(", ", net.getIPv4addr()));//IPv4 +// netVO.setInDropped(net.getInDrops());//入站丢包 +// netVO.setOutDropped(net.getCollisions());//出站丢包 +// netVO.setInSpeed(currentRx);//接收流量 +// netVO.setOutSpeed(currentTx);//发送流量 +// list.add(netVO); +// +// System.out.printf("网卡名称: \n", netVO.getName()); +// System.out.printf(" MAC地址: \n", netVO.getMac()); +// // 运行状态 +// System.out.printf("运行状态: \n", netVO.getStatus()); +//// System.out.printf("是否启用: \n", net.queryNetworkInterface().isUp() ? "UP" : "DOWN"); +// // 接口类型 +// System.out.printf("接口类型: \n", netVO.getType()); +// System.out.printf(" IPv4地址: \n", netVO.getIpV4()); +// // 接收数据 +// System.out.printf(" 接收字节数: %d (%.2f MB)\n", +// net.getBytesRecv(), net.getBytesRecv() / (1024.0 * 1024)); +// System.out.printf(" 接收包数: %d\n", net.getPacketsRecv()); +// System.out.printf(" 接收错误数: %d\n", net.getInErrors()); +// System.out.printf(" 接收丢包数: %d\n", net.getInDrops()); +// // 发送数据 +// System.out.printf(" 发送字节数: %d (%.2f MB)\n", +// net.getBytesSent(), net.getBytesSent() / (1024.0 * 1024)); +// System.out.printf(" 发送包数: %d\n", net.getPacketsSent()); +// System.out.printf(" 发送错误数: %d\n", net.getOutErrors()); +// System.out.printf(" 发送丢包数: %d\n", net.getCollisions()); +// // 1秒内的变化量 +// System.out.println(" --- 1秒内变化量 ---"); +// System.out.printf(" 接收总量(RX): %d bytes (%.2f MB)\n", +// currentRx, currentRx / (1024.0 * 1024)); +// System.out.printf(" 发送总量(TX): %d bytes (%.2f MB)\n", +// currentTx, currentTx / (1024.0 * 1024)); +// System.out.printf(" 接收速率: %.2f KB/s\n", rxDiff / 1024.0); +// System.out.printf(" 发送速率: %.2f KB/s\n", txDiff / 1024.0); +// System.out.println("----------------------"); +// } + } catch (Exception e) { + e.printStackTrace(); + } + return list; + } + + private static double bytesToMbps(long bytes) { + return bytes * 8.0 / 1_000_000; // bytes to megabits + } + +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/switchboard/SwitchBoardService.java b/src/main/java/com/tongran/agentserver/server/collect/switchboard/SwitchBoardService.java new file mode 100644 index 0000000..5b2aaa0 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/switchboard/SwitchBoardService.java @@ -0,0 +1,9 @@ +package com.tongran.agentserver.server.collect.switchboard; + +import com.tongran.agentserver.server.collect.vo.SwitchBoardVO; + +import java.util.List; + +public interface SwitchBoardService { + List query(); +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/switchboard/impl/SwitchBoardServiceImpl.java b/src/main/java/com/tongran/agentserver/server/collect/switchboard/impl/SwitchBoardServiceImpl.java new file mode 100644 index 0000000..8ca140b --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/switchboard/impl/SwitchBoardServiceImpl.java @@ -0,0 +1,190 @@ +package com.tongran.agentserver.server.collect.switchboard.impl; + +import com.tongran.agentserver.server.collect.switchboard.SwitchBoardService; +import com.tongran.agentserver.server.collect.vo.SwitchBoardVO; +import org.snmp4j.*; +import org.snmp4j.event.ResponseEvent; +import org.snmp4j.mp.SnmpConstants; +import org.snmp4j.smi.GenericAddress; +import org.snmp4j.smi.OID; +import org.snmp4j.smi.OctetString; +import org.snmp4j.smi.VariableBinding; +import org.snmp4j.transport.DefaultUdpTransportMapping; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Service +public class SwitchBoardServiceImpl implements SwitchBoardService { + private static final String COMMUNITY = "sjz_huiri"; // 默认community字符串 + private static final String SWITCH_IP = "172.16.15.2"; // 交换机IP + private static final int PORT = 161; // SNMP端口 + + @Override + public List query() { + List list = new ArrayList<>(); + SwitchBoardVO boardVO = SwitchBoardVO.builder().build(); + System.out.println("1111111111==================== 交换机流量信息 ===================="); + try { + // 1. 创建传输映射 + TransportMapping transport = new DefaultUdpTransportMapping(); + Snmp snmp = new Snmp(transport); + transport.listen(); + + // 2. 创建目标对象 + CommunityTarget target = new CommunityTarget(); + target.setCommunity(new OctetString(COMMUNITY)); + target.setAddress(GenericAddress.parse("udp:" + SWITCH_IP + "/" + PORT)); + target.setRetries(2); + target.setTimeout(5000); + target.setVersion(SnmpConstants.version2c); + + // 3. 获取交换机基本信息 + getSystemInfo(snmp, target); + + // 4. 获取接口信息 + list = getInterfaceInfo(snmp, target); + + snmp.close(); + } catch (Exception e) { + e.printStackTrace(); + } + return list; + } + + private static void getSystemInfo(Snmp snmp, Target target) throws IOException { + // 系统描述 OID + String[] systemOIDs = { + "1.3.6.1.2.1.1.1.0", // sysDescr + "1.3.6.1.2.1.1.5.0", // sysName + "1.3.6.1.2.1.1.6.0", // sysLocation + "1.3.6.1.2.1.1.4.0", // sysContact + "1.3.6.1.2.1.1.3.0" // sysUpTime + }; + + PDU pdu = new PDU(); + for (String oid : systemOIDs) { + pdu.add(new VariableBinding(new OID(oid))); + } + pdu.setType(PDU.GET); + + ResponseEvent event = snmp.send(pdu, target); + if (event != null && event.getResponse() != null) { + System.out.println("\n=== 交换机基本信息 ==="); + for (VariableBinding vb : event.getResponse().getVariableBindings()) { + System.out.printf("%-30s: %s%n", + getOIDDescription(vb.getOid().toString()), + vb.getVariable()); + } + } + } + + private static List getInterfaceInfo(Snmp snmp, Target target) throws IOException { + // 获取接口数量 + String ifNumberOID = "1.3.6.1.2.1.2.1.0"; + PDU pdu = new PDU(); + pdu.add(new VariableBinding(new OID(ifNumberOID))); + pdu.setType(PDU.GET); + + ResponseEvent event = snmp.send(pdu, target); + if (event == null || event.getResponse() == null) { + System.err.println("无法获取接口数量"); + return null; + } + + int ifNumber = event.getResponse().get(0).getVariable().toInt(); + System.out.println("\n=== 接口数量: " + ifNumber + " ==="); + + // 获取每个接口的信息 + String[] ifOIDs = { + "1.3.6.1.2.1.2.2.1.2", // ifDescr + "1.3.6.1.2.1.2.2.1.3", // ifType + "1.3.6.1.2.1.2.2.1.5", // ifSpeed + "1.3.6.1.2.1.2.2.1.8", // ifOperStatus + "1.3.6.1.2.1.2.2.1.10", // ifInOctets + "1.3.6.1.2.1.2.2.1.16" // ifOutOctets + }; + + System.out.printf("%-5s %-15s %-10s %-15s %-8s %-12s %-12s%n", + "Index", "Name", "Type", "Speed", "Status", "InBytes", "OutBytes"); + List list = new ArrayList<>(); + for (int i = 1; i <= ifNumber; i++) { + pdu = new PDU(); + for (String baseOID : ifOIDs) { + pdu.add(new VariableBinding(new OID(baseOID + "." + i))); + } + pdu.setType(PDU.GET); + + event = snmp.send(pdu, target); + if (event != null && event.getResponse() != null) { + VariableBinding[] vbs = event.getResponse().getVariableBindings().toArray(new VariableBinding[0]); + String ifName = vbs[0].getVariable().toString(); + String ifType = getInterfaceType(vbs[1].getVariable().toInt()); + String speed = formatSpeed(vbs[2].getVariable().toInt()); + String status = getOperStatus(vbs[3].getVariable().toInt()); + long inBytes = vbs[4].getVariable().toLong(); + long outBytes = vbs[5].getVariable().toLong(); + SwitchBoardVO boardVO = SwitchBoardVO.builder() + .name(ifName) + .type(ifType) + .status(status) + .inBytes(inBytes) + .outBytes(outBytes) + .build(); + list.add(boardVO); + System.out.printf("%-5d %-15s %-10s %-15s %-8s %-12d %-12d%n", + i, ifName, ifType, speed, status, inBytes, outBytes); + } + } + return list; + } + + // 辅助方法 + private static String getOIDDescription(String oid) { + switch(oid) { + case "1.3.6.1.2.1.1.1.0": return "系统描述"; + case "1.3.6.1.2.1.1.5.0": return "系统名称"; + case "1.3.6.1.2.1.1.6.0": return "物理位置"; + case "1.3.6.1.2.1.1.4.0": return "联系人"; + case "1.3.6.1.2.1.1.3.0": return "运行时间"; + default: return oid; + } + } + + private static String getInterfaceType(int type) { + switch(type) { + case 6: return "Ethernet"; + case 23: return "PPP"; + case 24: return "Loopback"; + case 53: return "VLAN"; + case 131: return "Tunnel"; + default: return "其他("+type+")"; + } + } + + private static String formatSpeed(int speed) { + if (speed >= 1000000000) { + return (speed / 1000000000) + " Gbps"; + } else if (speed >= 1000000) { + return (speed / 1000000) + " Mbps"; + } else if (speed >= 1000) { + return (speed / 1000) + " Kbps"; + } + return speed + " bps"; + } + + private static String getOperStatus(int status) { + switch(status) { + case 1: return "Up"; + case 2: return "Down"; + case 3: return "Testing"; + case 4: return "Unknown"; + case 5: return "Dormant"; + case 6: return "NotPresent"; + case 7: return "LowerLayerDown"; + default: return "未知"; + } + } +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/system/SystemService.java b/src/main/java/com/tongran/agentserver/server/collect/system/SystemService.java new file mode 100644 index 0000000..09ab25e --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/system/SystemService.java @@ -0,0 +1,8 @@ +package com.tongran.agentserver.server.collect.system; + +import com.tongran.agentserver.server.collect.vo.SystemVO; + +public interface SystemService { + SystemVO query(); + +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/system/impl/SystemServiceImpl.java b/src/main/java/com/tongran/agentserver/server/collect/system/impl/SystemServiceImpl.java new file mode 100644 index 0000000..c971aa0 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/system/impl/SystemServiceImpl.java @@ -0,0 +1,183 @@ +package com.tongran.agentserver.server.collect.system.impl; + +import com.tongran.agentserver.server.collect.system.SystemService; +import com.tongran.agentserver.server.collect.vo.SystemVO; +import com.tongran.agentserver.utils.AgentUtil; +import org.springframework.stereotype.Service; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.OperatingSystem; + +import java.io.*; +import java.lang.management.ManagementFactory; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@Service +public class SystemServiceImpl implements SystemService { + @Override + public SystemVO query() { + SystemVO systemVO = SystemVO.builder().build(); + try { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + CentralProcessor processor = hal.getProcessor(); + OperatingSystem os = si.getOperatingSystem(); + + System.out.println("========================================================="); + // 1. 操作系统基本信息 + System.out.println("=== 操作系统信息 ==="); + systemVO.setOs(os.getFamily()); //操作系统 + systemVO.setArch(os.getVersionInfo().getVersion() + " " + os.getBitness() + "位"); //操作系统架构 + systemVO.setUuid(AgentUtil.getMotherboardUUID()); + System.out.println("操作系统: " + systemVO.getOs()); + System.out.println("操作系统架构: " + systemVO.getArch()); + System.out.println("UUID: " + AgentUtil.getMotherboardUUID()); + + // 2. 进程信息 + System.out.println("\n=== 进程信息 ==="); + long maxProcesses = getMaxProcessesLinux(); + int runningProcesses = getRunningProcessesLinux(); + System.out.println("最大进程数: " + maxProcesses); + System.out.println("正在运行的进程数: " + runningProcesses); + systemVO.setMaxProc(maxProcesses); //最大进程数 + systemVO.setRunProcNum(runningProcesses); //正在运行的进程数 + + // 3. 登录用户数 + System.out.println("\n=== 登录用户 ==="); + systemVO.setUsersNum(os.getSessions().size()); //登录用户数 + System.out.println("登录用户数: " + systemVO.getUsersNum()); + + // 4. 磁盘信息 + System.out.println("\n=== 磁盘信息 ==="); + systemVO.setDiskSizeTotal(diskSpace()); //硬盘:总可用空间 + systemVO.setBootTime(systemBootTime(hal.getProcessor())); //系统启动时间 + systemVO.setUname(systemDescription(si)); //系统描述 + systemVO.setLocalTime(localTime()); //系统本地时间 + systemVO.setUpTime(systemUptime(os)); //系统正常运行时间 + System.out.println("硬盘:总可用空间: " + systemVO.getDiskSizeTotal()); + System.out.println("系统启动时间: " + systemVO.getBootTime()); + System.out.println("系统描述: " + systemVO.getUname()); + System.out.println("系统本地时间: " + systemVO.getLocalTime()); + System.out.println("系统正常运行时间: " + systemVO.getUpTime()); + } catch (IOException e) { + e.printStackTrace(); + } + return systemVO; + } + + // 获取最大进程数 + public int maxProc(){ + try { + // 执行 ulimit -u 命令(获取当前用户最大进程数) + Process process = Runtime.getRuntime().exec("ulimit -u"); + BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()) + ); + // 读取命令输出(示例输出:"1024") + String output = reader.readLine(); + reader.close(); + process.waitFor(); + // 解析结果(去除空格) + int userMaxProcesses = Integer.parseInt(output.trim()); + System.out.println("当前用户最大进程数(ulimit -u): " + userMaxProcesses); + return userMaxProcesses; + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + return 0; + } + + // 读取 /proc/sys/kernel/pid_max 获取最大进程数 + private static long getMaxProcessesLinux() throws IOException { + File pidMaxFile = new File("/proc/sys/kernel/pid_max"); + try (BufferedReader reader = new BufferedReader(new FileReader(pidMaxFile))) { + String line = reader.readLine().trim(); + return Long.parseLong(line); + } + } + + // 统计 /proc 下的数字目录数(每个目录对应一个进程) + private static int getRunningProcessesLinux() { + File procDir = new File("/proc"); + File[] files = procDir.listFiles(); + if (files == null) return 0; + + int count = 0; + for (File file : files) { + if (file.isDirectory() && file.getName().matches("\\d+")) { + count++; + } + } + return count; + } + + // 获取硬盘总可用空间 + public double diskSpace() { + double diskSizeTotal = 0; + File[] roots = File.listRoots(); +// System.out.println("\n===== 硬盘空间信息 ====="); + for (File root : roots) { + diskSizeTotal += (double) root.getFreeSpace() / (1024 * 1024 * 1024); +// System.out.printf("磁盘: %s\n", root.getAbsolutePath()); +// System.out.printf("总空间: %.2f GB\n", (double) root.getTotalSpace() / (1024 * 1024 * 1024)); +// System.out.printf("可用空间: %.2f GB\n", (double) root.getFreeSpace() / (1024 * 1024 * 1024)); +// System.out.printf("已用空间: %.2f GB\n", +// (double) (root.getTotalSpace() - root.getFreeSpace()) / (1024 * 1024 * 1024)); +// System.out.println("----------------------"); + } + return diskSizeTotal; + } + + // 获取系统启动时间 + public long systemBootTime(CentralProcessor processor) { + long[] systemCpuLoadTicks = processor.getSystemCpuLoadTicks(); + long bootTime = ManagementFactory.getRuntimeMXBean().getStartTime(); +// LocalDateTime bootDateTime = LocalDateTime.ofInstant( +// Instant.ofEpochMilli(bootTime), +// ZoneId.systemDefault() +// ); +// +// System.out.println("\n===== 系统启动时间 ====="); +// System.out.println("启动时间: " + +// bootDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + return bootTime; + } + + // 获取系统描述 + public String systemDescription(SystemInfo si) { + OperatingSystem os = si.getOperatingSystem(); + HardwareAbstractionLayer hal = si.getHardware(); +// System.out.println("\n===== 系统描述信息 ====="); +// System.out.println("操作系统: " + os.toString()); +// System.out.println("系统版本: " + os.getVersionInfo().toString()); +// System.out.println("处理器: " + hal.getProcessor().getProcessorIdentifier().getName()); +// System.out.println("物理内存: " + hal.getMemory().getTotal() / (1024 * 1024 * 1024) + " GB"); + return "操作系统: " + os.toString() + ",系统版本: " + os.getVersionInfo().toString() + "" + + ",处理器: " + hal.getProcessor().getProcessorIdentifier().getName() + + ",物理内存: " + hal.getMemory().getTotal() / (1024 * 1024 * 1024) + " GB"; + } + + // 获取系统本地时间 + public String localTime() { +// System.out.println("\n===== 系统本地时间 ====="); +// System.out.println("当前时间: " + +// LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); +// System.out.println("时区: " + ZoneId.systemDefault()); + return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + // 获取系统正常运行时间 + public long systemUptime(OperatingSystem os) { + long uptimeSeconds = os.getSystemUptime(); +// long days = uptimeSeconds / (24 * 3600); +// long hours = (uptimeSeconds % (24 * 3600)) / 3600; +// long minutes = (uptimeSeconds % 3600) / 60; +// long seconds = uptimeSeconds % 60; +// +// System.out.println("\n===== 系统运行时间 ====="); +// System.out.printf("系统已运行: %d天 %d小时 %d分钟 %d秒\n", days, hours, minutes, seconds); + return uptimeSeconds; + } +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/vo/CpuVO.java b/src/main/java/com/tongran/agentserver/server/collect/vo/CpuVO.java new file mode 100644 index 0000000..5ebd7e4 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/vo/CpuVO.java @@ -0,0 +1,57 @@ +package com.tongran.agentserver.server.collect.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "CPU信息") +public class CpuVO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "CPU1分钟负载") + private double avg1; + + @Schema(description = "CPU5分钟负载") + private double avg5; + + @Schema(description = "CPU15分钟负载") + private double avg15; + + @Schema(description = "CPU硬件中断提供服务时间") + private double interrupt; + + @Schema(description = "CPU使用率%") + private double uti; + + @Schema(description = "CPU数量") + private double num; + + @Schema(description = "CPU正常运行时间/秒") + private long normal; + + @Schema(description = "CPU空闲时间") + private double idle; + + @Schema(description = "CPU等待响应时间") + private double iowait; + + @Schema(description = "CPU系统时间") + private double system; + + @Schema(description = "CPU软件无响应时间") + private double noresp; + + @Schema(description = "CPU用户进程所花费的时间") + private double user; + + + +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/vo/DiskVO.java b/src/main/java/com/tongran/agentserver/server/collect/vo/DiskVO.java new file mode 100644 index 0000000..0261379 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/vo/DiskVO.java @@ -0,0 +1,47 @@ +package com.tongran.agentserver.server.collect.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "磁盘信息") +public class DiskVO implements Serializable { + private static final long serialVersionUID = 5L; + + @Schema(description = "磁盘名称") + private String name; + + @Schema(description = "序列号") + private String serial; + + @Schema(description = "磁盘大小(GB)") + private long total; + + @Schema(description = "磁盘写入速率") + private long writeSpeed; + + @Schema(description = "磁盘读取速率") + private long readSpeed; + + @Schema(description = "磁盘写入次数") + private long writeTimes; + + @Schema(description = "磁盘读取次数") + private long readTimes; + + @Schema(description = "磁盘写入字节") + private long writeBytes; + + @Schema(description = "磁盘读取字节") + private long readBytes; + + +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/vo/DockerVO.java b/src/main/java/com/tongran/agentserver/server/collect/vo/DockerVO.java new file mode 100644 index 0000000..5d9fbb7 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/vo/DockerVO.java @@ -0,0 +1,41 @@ +package com.tongran.agentserver.server.collect.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "容器信息") +public class DockerVO implements Serializable { + private static final long serialVersionUID = 4L; + + @Schema(description = "容器ID") + private String id; + + @Schema(description = "容器名称") + private String name; + + @Schema(description = "容器状态") + private String status; + + @Schema(description = "容器CPU使用率") + private String cpuUtil; + + @Schema(description = "容器内存使用率") + private String memUtil; + + @Schema(description = "容器网络接收速率") + private String netInSpeed; + + @Schema(description = "容器网络发送速率") + private String netOutSpeed; + + +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/vo/MemoryVO.java b/src/main/java/com/tongran/agentserver/server/collect/vo/MemoryVO.java new file mode 100644 index 0000000..893533d --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/vo/MemoryVO.java @@ -0,0 +1,37 @@ +package com.tongran.agentserver.server.collect.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "内存信息") +public class MemoryVO implements Serializable { + private static final long serialVersionUID = 2L; + + @Schema(description = "交换卷/文件的可用空间(字节)") + private double swapSizeFree; + + @Schema(description = "内存利用率") + private double untilzation; + + @Schema(description = "可用交换空间百分比") + private double swapSizePercent; + + @Schema(description = "可用内存") + private long available; + + @Schema(description = "可用内存百分比") + private double percent; + + @Schema(description = "总内存") + private long total; + +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/vo/NetVO.java b/src/main/java/com/tongran/agentserver/server/collect/vo/NetVO.java new file mode 100644 index 0000000..69e82e2 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/vo/NetVO.java @@ -0,0 +1,49 @@ +package com.tongran.agentserver.server.collect.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "网络信息") +public class NetVO implements Serializable { + private static final long serialVersionUID = 6L; + + @Schema(description = "网卡名称") + private String name; + + @Schema(description = "MAC") + private String mac; + + @Schema(description = "运行状态") + private String status; + + @Schema(description = "接口类型") + private String type; + + @Schema(description = "IPv4") + private String ipV4; + + @Schema(description = "入站丢包") + private long inDropped; + + @Schema(description = "出站丢包") + private long outDropped; + + @Schema(description = "发送流量") + private String outSpeed; + + @Schema(description = "接收流量") + private String inSpeed; + + @Schema(description = "接收速度") + private String speed; + +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/vo/PointVO.java b/src/main/java/com/tongran/agentserver/server/collect/vo/PointVO.java new file mode 100644 index 0000000..c8a5049 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/vo/PointVO.java @@ -0,0 +1,33 @@ +package com.tongran.agentserver.server.collect.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "挂载点信息") +public class PointVO implements Serializable { + private static final long serialVersionUID = 7L; + + @Schema(description = "挂载点") + private String mount; + + @Schema(description = "文件系统类型") + private String vfsType; + + @Schema(description = "可用空间") + private long vfsFree; + + @Schema(description = "总空间") + private long vfsTotal; + + @Schema(description = "空间利用率") + private double vfsUtil; +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/vo/SwitchBoardVO.java b/src/main/java/com/tongran/agentserver/server/collect/vo/SwitchBoardVO.java new file mode 100644 index 0000000..ded7287 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/vo/SwitchBoardVO.java @@ -0,0 +1,35 @@ +package com.tongran.agentserver.server.collect.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "交换机信息") +public class SwitchBoardVO implements Serializable { + private static final long serialVersionUID = 8L; + + @Schema(description = "网口名称") + private String name; + + @Schema(description = "网口类型") + private String type; + + @Schema(description = "网口状态") + private String status; + + @Schema(description = "接收流量") + private long inBytes; + + @Schema(description = "发送流量") + private long outBytes; + + +} diff --git a/src/main/java/com/tongran/agentserver/server/collect/vo/SystemVO.java b/src/main/java/com/tongran/agentserver/server/collect/vo/SystemVO.java new file mode 100644 index 0000000..2c0441f --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/collect/vo/SystemVO.java @@ -0,0 +1,58 @@ +package com.tongran.agentserver.server.collect.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "系统信息") +public class SystemVO implements Serializable { + private static final long serialVersionUID = 3L; + + @Schema(description = "操作系统") + private String os; + + @Schema(description = "操作系统架构") + private String arch; + + @Schema(description = "最大进程数") + private long maxProc; + + @Schema(description = "正在运行的进程数") + private int runProcNum; + + @Schema(description = "登录用户数") + private int usersNum; + + @Schema(description = "硬盘:总可用空间") + private double diskSizeTotal; + + @Schema(description = "系统启动时间") + private long bootTime; + + @Schema(description = "系统描述") + private String uname; + + @Schema(description = "系统本地时间") + private String localTime; + + @Schema(description = "系统正常运行时间") + private long upTime; + + @Schema(description = "进程数") + private double procNum; + + private String uuid; + + @Schema(description = "时间戳") + private long timeStamp; + + +} diff --git a/src/main/java/com/tongran/agentserver/server/netty/NettyTcpClient.java b/src/main/java/com/tongran/agentserver/server/netty/NettyTcpClient.java new file mode 100644 index 0000000..9342cae --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/netty/NettyTcpClient.java @@ -0,0 +1,127 @@ +package com.tongran.agentserver.server.netty; + +import com.tongran.agentserver.server.netty.config.AgentNettyConfig; +import com.tongran.agentserver.server.netty.handler.DecoderHandler; +import com.tongran.agentserver.server.netty.handler.NettyClientHandler; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.string.StringEncoder; +import io.netty.handler.timeout.IdleStateHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.annotation.Resource; +import java.util.concurrent.TimeUnit; + +@Component +public class NettyTcpClient { + + private static final Logger logger = LoggerFactory.getLogger(NettyTcpClient.class); + + private EventLoopGroup group = new NioEventLoopGroup(); + + private Channel channel; + + @Resource + private AgentNettyConfig config; + + // 目标服务器IP和端口 +// private final String host = "www.tz2.xyz"; // 替换为实际IP +// private final int port = 6610; // 替换为实际端口 + + /** + * 启动客户端连接 + */ + @PostConstruct + public void start() { + try { + connect(); + } catch (Exception e) { + logger.error("Netty客户端启动失败", e); + } + } + + public void connect() throws InterruptedException { + Bootstrap bootstrap = new Bootstrap(); + bootstrap.group(group) + .channel(NioSocketChannel.class) + .option(ChannelOption.TCP_NODELAY, true) + .option(ChannelOption.SO_KEEPALIVE, true) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) { + ChannelPipeline pipeline = ch.pipeline(); + // 添加编解码器 + pipeline.addLast("decoder", new DecoderHandler()); + pipeline.addLast("encoder", new StringEncoder()); + // 添加心跳机制 + pipeline.addLast("idleStateHandler", + new IdleStateHandler(0, 0, 90, TimeUnit.SECONDS)); + // 添加自定义处理器 + pipeline.addLast("clientHandler", new NettyClientHandler()); + } + }); + + // 连接服务器 + ChannelFuture future = bootstrap.connect(config.getHost(), config.getPort()).sync(); + + // 监听连接状态 + future.addListener((ChannelFutureListener) f -> { + if (f.isSuccess()) { + logger.info("成功连接到TCP服务器 {}:{}", config.getHost(), config.getPort()); + channel = f.channel(); + } else { + logger.error("连接TCP服务器失败 {}:{}, 5秒后尝试重连...", config.getHost(), config.getPort()); + f.channel().eventLoop().schedule(() -> { + try { + connect(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + }, 5, TimeUnit.SECONDS); + } + }); + } + + /** + * 发送消息 + * @param message 要发送的消息内容 + */ + public void sendMessage(String message) { + if (channel != null && channel.isActive()) { + channel.writeAndFlush(message); + logger.info("发送消息: {}", message); + } else { + logger.warn("TCP连接未建立,消息发送失败: {}", message); + } + } + + /** + * 关闭连接 + */ + @PreDestroy + public void stop() { + if (channel != null) { + channel.close(); + } + group.shutdownGracefully(); + logger.info("Netty客户端已关闭"); + } + + /** + * 检查连接状态 + * @return true表示连接正常 + */ + public boolean isConnected() { + return channel != null && channel.isActive(); + } + + +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/server/netty/config/AgentNettyConfig.java b/src/main/java/com/tongran/agentserver/server/netty/config/AgentNettyConfig.java new file mode 100644 index 0000000..3b4905f --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/netty/config/AgentNettyConfig.java @@ -0,0 +1,16 @@ +package com.tongran.agentserver.server.netty.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "netty.server") +public class AgentNettyConfig { + + private String host; + + private int port; + +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/server/netty/handler/DecoderHandler.java b/src/main/java/com/tongran/agentserver/server/netty/handler/DecoderHandler.java new file mode 100644 index 0000000..3b0e864 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/netty/handler/DecoderHandler.java @@ -0,0 +1,55 @@ +package com.tongran.agentserver.server.netty.handler; + +import cn.hutool.cache.Cache; +import cn.hutool.cache.CacheUtil; +import com.tongran.agentserver.utils.AssertLog; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.util.CharsetUtil; +import org.springframework.stereotype.Component; + +@Component +@ChannelHandler.Sharable +public class DecoderHandler extends ChannelInboundHandlerAdapter { + + + + // 用来临时保留没有处理过的请求报文 + private final Cache lruCache = CacheUtil.newLRUCache(3000); + + /** + * 消息解码器 + */ + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + // 检查是否为 ByteBuf(未解码的原始数据) + if (msg instanceof ByteBuf) { + ByteBuf byteBuf = (ByteBuf) msg; + String messages = byteBuf.toString(CharsetUtil.UTF_8); // 指定字符集解码 + AssertLog.info("<<[up]:[up-content]==>{}", messages); + +// JSONObject jsonObject = JSONObject.parseObject(messages); +// String clientId = jsonObject.getString("clientId"); +// Message electricMessage = Message.builder().build(); +// electricMessage.setClientId(clientId); +// JSONObject object = new JSONObject(); +// object.put("dateType","register"); +// object.put("stats","1"); +// electricMessage.setContent(object.toString()); +// ctx.fireChannelRead(electricMessage);//传递到下一个handler + + byteBuf.release(); // 释放 ByteBuf 资源(重要!) + } else { + System.out.println("Unexpected message type: " + msg.getClass()); + } + + } + + public byte[] subByte(byte[] b, int off, int length) { + byte[] bytes = new byte[length]; + System.arraycopy(b, off, bytes, 0, length); + return bytes; + } +} diff --git a/src/main/java/com/tongran/agentserver/server/netty/handler/NettyClientHandler.java b/src/main/java/com/tongran/agentserver/server/netty/handler/NettyClientHandler.java new file mode 100644 index 0000000..f28c287 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/netty/handler/NettyClientHandler.java @@ -0,0 +1,214 @@ +package com.tongran.agentserver.server.netty.handler; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.tongran.agentserver.server.netty.model.Message; +import com.tongran.agentserver.utils.AgentUtil; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.string.StringEncoder; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import io.netty.util.AttributeKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +@ChannelHandler.Sharable +public class NettyClientHandler extends SimpleChannelInboundHandler { + + private static final Logger logger = LoggerFactory.getLogger(NettyClientHandler.class); + + // 目标服务器 IP 和端口(从 Channel 的 RemoteAddress 动态获取) + private String serverIp; + private int serverPort; + + // 重连状态标记(原子变量保证线程安全) + private AtomicBoolean isReconnecting = new AtomicBoolean(false); + private int retryCount = 0; // 当前重试次数 + + // 重连策略配置(可根据需求调整) + private int maxRetries = 3; // 最大重试次数(3次) + private long initialInterval = 1000; // 初始重连间隔(1秒) + private long intervalMultiplier = 2; // 间隔倍数(指数退避) + private long maxInterval = 4000; // 最大重连间隔(4秒) + + // 异步调度器(独立线程池,避免阻塞 Netty I/O 线程) + private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + + // AttributeKey 用于传递重连上下文(可选) + private static final AttributeKey RECONNECT_HANDLER_KEY = + AttributeKey.valueOf("nettyClientHandler"); + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Message msg) { + logger.info("收到服务器消息: {}", msg); + // 这里可以添加消息处理逻辑 + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof IdleStateEvent) { + IdleStateEvent event = (IdleStateEvent) evt; + if (event.state() == IdleState.WRITER_IDLE) { + // 发送心跳包 + JSONObject object = new JSONObject(); + object.put("strength","31"); + String clientId = AgentUtil.getMotherboardUUID(); + Message message = Message.builder().clientId(clientId).dataType("HEARTBEAT").data(object.toString()).build(); + // 将对象转为 JSON 字符串 + String json = "agent-tcp:"+JSON.toJSONString(message)+"@tong-ran"; + System.out.println(json); + ctx.writeAndFlush(json); + logger.debug("发送心跳包"); + } + } else { + super.userEventTriggered(ctx, evt); + } + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + logger.info("与服务器建立连接: {}", ctx.channel().remoteAddress()); + // 连接建立时,记录目标服务器 IP 和端口(从 RemoteAddress 解析) + InetSocketAddress remoteAddr = (InetSocketAddress) ctx.channel().remoteAddress(); + this.serverIp = remoteAddr.getHostString(); + this.serverPort = remoteAddr.getPort(); + System.out.println("连接建立成功,目标服务器: " + serverIp + ":" + serverPort); + retryCount = 0; // 连接成功时重置重试次数 + + // 连接建立后可以发送登录认证消息 + String clientId = AgentUtil.getMotherboardUUID(); + JSONObject object = new JSONObject(); + object.put("uuid",clientId); + Message message = Message.builder().clientId(clientId).dataType("LOGIN").data(object.toString()).build(); + // 将对象转为 JSON 字符串 + String json = "agent-tcp:"+JSON.toJSONString(message)+"@tong-ran"; + System.out.println("连接建立成功,发送登录认证="+json); + ctx.writeAndFlush(json); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) { + logger.warn("与服务器断开连接: {}", ctx.channel().remoteAddress()); + // 连接断开时触发重连逻辑 + System.out.println("连接断开,触发重连..."); + if (isReconnecting.compareAndSet(false, true)) { + startReconnectTask(ctx); // 启动异步重连任务 + } + } + + /** + * 启动异步重连任务(指数退避策略) + */ + private void startReconnectTask(ChannelHandlerContext ctx) { + // 检查是否超过最大重试次数 + if (retryCount >= maxRetries) { + System.out.println("已达到最大重试次数(" + maxRetries + "),停止重连"); + isReconnecting.set(false); + return; + } + + // 计算当前重连间隔(指数退避公式:initialInterval * (intervalMultiplier ^ retryCount)) + long currentInterval = calculateCurrentInterval(); + System.out.printf("第 %d 次重连,将在 %dms 后尝试...%n", retryCount + 1, currentInterval); + + // 调度重连任务(使用独立线程池) + ScheduledFuture reconnectFuture = scheduler.schedule(() -> { + try { + // 关闭已断开的 Channel(避免资源泄漏) + if (ctx.channel().isActive()) { + ctx.channel().close().sync(); + } + + // 重新初始化 Bootstrap 并连接服务器 + Bootstrap bootstrap = new Bootstrap(); + bootstrap.group(ctx.channel().eventLoop()) // 复用原 EventLoopGroup + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) { + // 重新添加编解码器和业务处理器(与初始连接一致) + ch.pipeline().addLast(new DecoderHandler()); // 自定义解码器 + ch.pipeline().addLast(new StringEncoder()); // 自定义编码器 + ch.pipeline().addLast(new NettyClientHandler()); // 绑定重连处理器 + // 其他业务处理器... + } + }); + + // 执行连接(阻塞直到完成) + ChannelFuture connectFuture = bootstrap.connect(serverIp, serverPort).sync(); + Channel newChannel = connectFuture.channel(); + + // 重连成功后的处理 + onReconnectSuccess(newChannel); + } catch (Exception e) { + // 重连失败处理 + onReconnectFailure(ctx,e); + } + }, currentInterval, TimeUnit.MILLISECONDS); + } + + /** + * 计算当前重连间隔(指数退避) + */ + private long calculateCurrentInterval() { + int currentRetry = retryCount; + long interval = (long) (initialInterval * Math.pow(intervalMultiplier, currentRetry)); + return Math.min(interval, maxInterval); // 限制最大间隔 + } + + /** + * 重连成功回调 + */ + private void onReconnectSuccess(Channel newChannel) { + System.out.println("重连成功!新 Channel ID: " + newChannel.id().asLongText()); + // 重置重连状态 + retryCount = 0; + isReconnecting.set(false); + // 可选:同步业务状态(如会话恢复) + } + + /** + * 重连失败回调 + */ + private void onReconnectFailure(ChannelHandlerContext ctx,Exception e) { + retryCount++; // 重试次数+1 + System.err.printf("重连失败(第 %d 次): %s%n", retryCount, e.getMessage()); + + // 检查是否超过最大重试次数 + if (retryCount >= maxRetries) { + System.out.println("已达到最大重试次数(" + maxRetries + "),停止重连"); + isReconnecting.set(false); + return; + } + + // 继续下一次重连(触发 channelInactive 重新调度) + if (ctx != null) { + startReconnectTask(ctx); + } + } + + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + logger.error("通信异常", cause); + ctx.close(); + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) { + // 清理资源(如关闭调度器) + scheduler.shutdown(); + } + + +} \ No newline at end of file diff --git a/src/main/java/com/tongran/agentserver/server/netty/model/Message.java b/src/main/java/com/tongran/agentserver/server/netty/model/Message.java new file mode 100644 index 0000000..bbf7542 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/server/netty/model/Message.java @@ -0,0 +1,37 @@ +package com.tongran.agentserver.server.netty.model; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author egrias + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Message implements Serializable { + + private static final long serialVersionUID = -1267013167162440610L; + + /** + * clientId + */ + private String clientId; + + /** + * 数据类型:LOGIN、HEARTBEAT、CPU、MEMORY、SYSTEM、POINT、NET、DISK、DOCKER、SWITCHBOARD + */ + private String dataType; + + /** + * 发送内容 + */ + private String data; + +} diff --git a/src/main/java/com/tongran/agentserver/utils/AgentUtil.java b/src/main/java/com/tongran/agentserver/utils/AgentUtil.java new file mode 100644 index 0000000..62cf4e4 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/utils/AgentUtil.java @@ -0,0 +1,20 @@ +package com.tongran.agentserver.utils; + +import org.apache.commons.lang3.StringUtils; +import oshi.SystemInfo; +import oshi.hardware.Baseboard; +import oshi.hardware.HardwareAbstractionLayer; + +public class AgentUtil { + + public static String getMotherboardUUID() { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + Baseboard baseboard = hal.getComputerSystem().getBaseboard(); + if(StringUtils.isNotBlank(baseboard.getSerialNumber())){ + return baseboard.getSerialNumber(); + } + return "client-001"; + } + +} diff --git a/src/main/java/com/tongran/agentserver/utils/AssertLog.java b/src/main/java/com/tongran/agentserver/utils/AssertLog.java new file mode 100644 index 0000000..1d38fe8 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/utils/AssertLog.java @@ -0,0 +1,73 @@ +package com.tongran.agentserver.utils; + +import lombok.extern.slf4j.Slf4j; + +/** + * 日志断言类 + * + * @author LJ + * + */ +@Slf4j +public class AssertLog { + + /** + * 打印Info 日志 + * + * @param format + * @param arguments + */ + public static void info(String format, Object... arguments) { + if (log.isInfoEnabled()) { + log.info(format, arguments); + } + } + + /** + * 打印Debug 日志 + * + * @param format + * @param arguments + */ + public static void debug(String format, Object... arguments) { + if (log.isDebugEnabled()) { + log.debug(format, arguments); + } + } + + /** + * 打印Error 日志 + * + * @param format + * @param arguments + */ + public static void error(String format, Object... arguments) { + if (log.isErrorEnabled()) { + log.error(format, arguments); + } + } + + /** + * 打印Trace 日志 + * + * @param format + * @param arguments + */ + public static void trace(String format, Object... arguments) { + if (log.isTraceEnabled()) { + log.trace(format, arguments); + } + } + + /** + * 打印Warn 日志 + * + * @param format + * @param arguments + */ + public static void warn(String format, Object... arguments) { + if (log.isWarnEnabled()) { + log.warn(format, arguments); + } + } +} diff --git a/src/main/java/com/tongran/agentserver/utils/EscapeUtil.java b/src/main/java/com/tongran/agentserver/utils/EscapeUtil.java new file mode 100644 index 0000000..a5b84f5 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/utils/EscapeUtil.java @@ -0,0 +1,227 @@ +package com.tongran.agentserver.utils; + +import org.apache.commons.lang3.StringUtils; +import oshi.hardware.NetworkIF; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.*; + +public class EscapeUtil { + + /** + * 发送数据转义 + */ + public static String escape(String zoneCode, String terminalAddress, String groupAdr, String sendContent) { +// if (StringUtils.isNotBlank(sendContent)) { +// StringBuilder content = new StringBuilder(); +// StringBuilder signData = new StringBuilder(); +// int fixed = 34; +// int len = sendContent.length() / 2; +// String dataLen = fill(Integer.toHexString(len), 4); +// String pwd = "00000000000000000000000000000000"; +// // 长度转二进制补10 转十六进制 +// String head = Integer.toBinaryString(len + fixed) + "10"; +// head = fill(Integer.toHexString(Integer.parseInt(head, 2)), 4); +// content.append("68");// 起始字符 +// content.append(head + head);// 长度 +// content.append("68");// 起始字符 +// signData.append("6b");// 控制域 +// signData.append(zoneCode);// 行政区划码 A1 +// signData.append(terminalAddress);// 终端地址 A2 +// signData.append(groupAdr);// 主站地址和组地址标志 A3 +// signData.append("10");// AFN +// signData.append("64");// 帧序列域 +// signData.append("0000");// P0 +// signData.append("0100");// F1 +// signData.append("02");// 端口号 +// signData.append("6b");// 透明转发通信控制字 +// signData.append("00");// 透明转发接收等待报文超时时间 +// signData.append("00");// 透明转发接收等待字节超时时间 +// signData.append(dataLen);// 转发的数据内容长度 +// signData.append(sendContent); +// signData.append(pwd); +// content.append(signData); +// String cs = SeqUtil.makeChecksum(signData.toString()); +// content.append(cs);// CS +// content.append("16");// 结束字符 +// return content.toString(); +// } + return null; + } + + + public static String increase(String data, int len) { + int length = data.length(); + for (int i = 0; i < len - length; i++) { + data = "0" + data; + } + return data; + } + + /** + * 补零,反转 + */ + public static String fill(String data, int len) { + int length = data.length(); + for (int i = 0; i < len - length; i++) { + data = "0" + data; + } + if (len % 2 == 0) { + List list = new ArrayList<>(); + for (int i = 0; i < len; i += 2) { + list.add(data.substring(i, i + 2)); + } + Collections.reverse(list); + data = StringUtils.join(list.toArray()); + } + return data; + } + + public static String countervailing(String data, int len) { + int length = data.length(); + for (int i = 0; i < len - length; i++) { + data = "f" + data; + } + if (len % 2 == 0) { + List list = new ArrayList<>(); + for (int i = 0; i < len; i += 2) { + list.add(data.substring(i, i + 2)); + } + Collections.reverse(list); + data = StringUtils.join(list.toArray()); + } + return data; + } + + /** + * 数据域+0x33转义 + */ + public static String change(String data) { + if (StringUtils.isNotBlank(data) && data.length() > 2) { + int len = data.length(); + List list = new ArrayList<>(); + for (int i = 0; i < len; i += 2) { + int num = new BigInteger(data.substring(i, i + 2), 16).intValue() + 0x33; + if (num >= 256) { + num = num - 256; + } + String temp = Integer.toHexString(num); + if (temp.length() < 2) { + temp = "0" + temp; + } + list.add(temp); + } + return StringUtils.join(list.toArray()); + } + return null; + } + + public static byte[] hexStringToByteArray(String str) { + if (str.startsWith("0x")) { // Get rid of potential prefix + str = str.substring(2); + } + + if (str.length() % 2 != 0) { // If string is not of even length + str = '0' + str; // Assume leading zeroes were left out + } + + byte[] result = new byte[str.length() / 2]; + for (int i = 0; i < str.length(); i += 2) { + String nextByte = str.charAt(i) + "" + str.charAt(i + 1); + // To avoid overflow, parse as int and truncate: + result[i / 2] = (byte) Integer.parseInt(nextByte, 16); + } + return result; + } + + public static String createSign(SortedMap packageParams, String API_KEY) throws Exception { + StringBuffer sb = new StringBuffer(); + Set> es = packageParams.entrySet(); + Iterator it = es.iterator(); + + while (it.hasNext()) { + Map.Entry entry = (Map.Entry) it.next(); + String k = (String) entry.getKey(); + String v = (String) entry.getValue(); + if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { + sb.append(k + "=" + v + "&"); + } + } + + sb.append("key=" + API_KEY); + String sign = MD5(sb.toString()).toUpperCase(); + return sign; + } + + public static String MD5(String data) throws Exception { + MessageDigest md = MessageDigest.getInstance("MD5"); + byte[] array = md.digest(data.getBytes("UTF-8")); + StringBuilder sb = new StringBuilder(); + byte[] var4 = array; + int var5 = array.length; + + for (int var6 = 0; var6 < var5; ++var6) { + byte item = var4[var6]; + sb.append(Integer.toHexString(item & 255 | 256).substring(1, 3)); + } + + return sb.toString().toUpperCase(); + } + + public static String getRequestXml(SortedMap parameters) { + StringBuffer sb = new StringBuffer(); + sb.append(""); + Set> es = parameters.entrySet(); + Iterator it = es.iterator(); + + while (true) { + while (it.hasNext()) { + Map.Entry entry = (Map.Entry) it.next(); + String k = (String) entry.getKey(); + String v = (String) entry.getValue(); + if (!"attach".equalsIgnoreCase(k) && !"body".equalsIgnoreCase(k) && !"sign".equalsIgnoreCase(k)) { + sb.append("<" + k + ">" + v + ""); + } else { + sb.append("<" + k + ">" + v + ""); + } + } + + sb.append(""); + return new String(sb.toString().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1); + } + } + + public static double bytesToMbps(long bytes) { + return bytes * 8.0 / 1_000_000; // bytes to megabits + } + + public static String getInterfaceType(NetworkIF net) { + String name = net.getName().toLowerCase(); + String displayName = net.getDisplayName().toLowerCase(); + + if (name.startsWith("eth") || name.startsWith("en") || displayName.contains("ethernet")) { + return "Ethernet"; + } else if (name.startsWith("wlan") || name.startsWith("wl") || displayName.contains("wireless")) { + return "Wi-Fi"; + } else if (name.startsWith("lo") || displayName.contains("loopback")) { + return "Loopback"; + } else if (name.startsWith("ppp") || displayName.contains("point-to-point")) { + return "PPP"; + } else if (name.startsWith("vmnet") || displayName.contains("virtual")) { + return "Virtual"; + } else if (name.startsWith("tun") || name.startsWith("tap")) { + return "TUN/TAP"; + } else if (name.startsWith("br") || displayName.contains("bridge")) { + return "Bridge"; + } else if (name.startsWith("bond") || displayName.contains("bond")) { + return "Bond"; + } else { + return "Unknown"; + } + } + + + +} diff --git a/src/main/java/com/tongran/agentserver/utils/R.java b/src/main/java/com/tongran/agentserver/utils/R.java new file mode 100644 index 0000000..bb63f91 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/utils/R.java @@ -0,0 +1,79 @@ +package com.tongran.agentserver.utils; + +import cn.hutool.extra.spring.SpringUtil; +import com.tongran.agentserver.core.exception.code.ErrorCode; +import com.tongran.agentserver.core.exception.code.GlobalErrorCode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author BAO + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "接口交互统一数据返回标准") +public class R implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "返回代码") + private Integer code; + + @Schema(description = "消息描述") + private String msg; + + @Schema(description = "结果对象") + private T data; + + @Schema(description = "接口环境") + private String active; + + public static R success(String msg, T t) { + R r = new R<>(); + r.setData(t); + r.setMsg(msg); + r.setCode(GlobalErrorCode.SUCCESS.getCode()); + r.setActive(SpringUtil.getActiveProfile()); + return r; + } + + public static R success(T t) { + return R.success(GlobalErrorCode.SUCCESS.getMsg(), t); + } + + public static R success() { + return R.success(null); + } + + public static R error(String msg, Integer code) { + R r = new R<>(); + r.setMsg(msg); + r.setCode(code); + r.setActive(SpringUtil.getActiveProfile()); + return r; + } + + public static R error(Integer code, String msg) { + return error(msg, code); + } + + public static R error(ErrorCode err) { + return R.error(err.getMsg(), err.getCode()); + } + + public static R error() { + return R.error(GlobalErrorCode.ERROR.getMsg(), GlobalErrorCode.ERROR.getCode()); + } + + public static R error(String msg) { + return R.error(msg, GlobalErrorCode.ERROR.getCode()); + } + +} diff --git a/src/main/java/com/tongran/agentserver/utils/SwitchInfoFetcher.java b/src/main/java/com/tongran/agentserver/utils/SwitchInfoFetcher.java new file mode 100644 index 0000000..1728d47 --- /dev/null +++ b/src/main/java/com/tongran/agentserver/utils/SwitchInfoFetcher.java @@ -0,0 +1,172 @@ +package com.tongran.agentserver.utils; + +import org.snmp4j.*; +import org.snmp4j.event.ResponseEvent; +import org.snmp4j.mp.SnmpConstants; +import org.snmp4j.smi.GenericAddress; +import org.snmp4j.smi.OID; +import org.snmp4j.smi.OctetString; +import org.snmp4j.smi.VariableBinding; +import org.snmp4j.transport.DefaultUdpTransportMapping; + +import java.io.IOException; + +public class SwitchInfoFetcher { + + private static final String COMMUNITY = "public"; // 默认community字符串 + private static final String SWITCH_IP = "183.249.21.1"; // 交换机IP + private static final int PORT = 62161; // SNMP端口 + + public static void main(String[] args) { + try { + // 1. 创建传输映射 + TransportMapping transport = new DefaultUdpTransportMapping(); + Snmp snmp = new Snmp(transport); + transport.listen(); + + // 2. 创建目标对象 + CommunityTarget target = new CommunityTarget(); + target.setCommunity(new OctetString(COMMUNITY)); + target.setAddress(GenericAddress.parse("udp:" + SWITCH_IP + "/" + PORT)); + target.setRetries(2); + target.setTimeout(5000); + target.setVersion(SnmpConstants.version2c); + + // 3. 获取交换机基本信息 + getSystemInfo(snmp, target); + + // 4. 获取接口信息 + getInterfaceInfo(snmp, target); + + snmp.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void getSystemInfo(Snmp snmp, Target target) throws IOException { + // 系统描述 OID + String[] systemOIDs = { + "1.3.6.1.2.1.1.1.0", // sysDescr + "1.3.6.1.2.1.1.5.0", // sysName + "1.3.6.1.2.1.1.6.0", // sysLocation + "1.3.6.1.2.1.1.4.0", // sysContact + "1.3.6.1.2.1.1.3.0" // sysUpTime + }; + + PDU pdu = new PDU(); + for (String oid : systemOIDs) { + pdu.add(new VariableBinding(new OID(oid))); + } + pdu.setType(PDU.GET); + + ResponseEvent event = snmp.send(pdu, target); + if (event != null && event.getResponse() != null) { + System.out.println("\n=== 交换机基本信息 ==="); + for (VariableBinding vb : event.getResponse().getVariableBindings()) { + System.out.printf("%-30s: %s%n", + getOIDDescription(vb.getOid().toString()), + vb.getVariable()); + } + } + } + + private static void getInterfaceInfo(Snmp snmp, Target target) throws IOException { + // 获取接口数量 + String ifNumberOID = "1.3.6.1.2.1.2.1.0"; + PDU pdu = new PDU(); + pdu.add(new VariableBinding(new OID(ifNumberOID))); + pdu.setType(PDU.GET); + + ResponseEvent event = snmp.send(pdu, target); + if (event == null || event.getResponse() == null) { + System.err.println("无法获取接口数量"); + return; + } + + int ifNumber = event.getResponse().get(0).getVariable().toInt(); + System.out.println("\n=== 接口数量: " + ifNumber + " ==="); + + // 获取每个接口的信息 + String[] ifOIDs = { + "1.3.6.1.2.1.2.2.1.2", // ifDescr + "1.3.6.1.2.1.2.2.1.3", // ifType + "1.3.6.1.2.1.2.2.1.5", // ifSpeed + "1.3.6.1.2.1.2.2.1.8", // ifOperStatus + "1.3.6.1.2.1.2.2.1.10", // ifInOctets + "1.3.6.1.2.1.2.2.1.16" // ifOutOctets + }; + + System.out.printf("%-5s %-15s %-10s %-15s %-8s %-12s %-12s%n", + "Index", "Name", "Type", "Speed", "Status", "InBytes", "OutBytes"); + + for (int i = 1; i <= ifNumber; i++) { + pdu = new PDU(); + for (String baseOID : ifOIDs) { + pdu.add(new VariableBinding(new OID(baseOID + "." + i))); + } + pdu.setType(PDU.GET); + + event = snmp.send(pdu, target); + if (event != null && event.getResponse() != null) { + VariableBinding[] vbs = event.getResponse().getVariableBindings().toArray(new VariableBinding[0]); + String ifName = vbs[0].getVariable().toString(); + String ifType = getInterfaceType(vbs[1].getVariable().toInt()); + String speed = formatSpeed(vbs[2].getVariable().toInt()); + String status = getOperStatus(vbs[3].getVariable().toInt()); + long inBytes = vbs[4].getVariable().toLong(); + long outBytes = vbs[5].getVariable().toLong(); + + System.out.printf("%-5d %-15s %-10s %-15s %-8s %-12d %-12d%n", + i, ifName, ifType, speed, status, inBytes, outBytes); + } + } + } + + // 辅助方法 + private static String getOIDDescription(String oid) { + switch(oid) { + case "1.3.6.1.2.1.1.1.0": return "系统描述"; + case "1.3.6.1.2.1.1.5.0": return "系统名称"; + case "1.3.6.1.2.1.1.6.0": return "物理位置"; + case "1.3.6.1.2.1.1.4.0": return "联系人"; + case "1.3.6.1.2.1.1.3.0": return "运行时间"; + default: return oid; + } + } + + private static String getInterfaceType(int type) { + switch(type) { + case 6: return "Ethernet"; + case 23: return "PPP"; + case 24: return "Loopback"; + case 53: return "VLAN"; + case 131: return "Tunnel"; + default: return "其他("+type+")"; + } + } + + private static String formatSpeed(int speed) { + if (speed >= 1000000000) { + return (speed / 1000000000) + " Gbps"; + } else if (speed >= 1000000) { + return (speed / 1000000) + " Mbps"; + } else if (speed >= 1000) { + return (speed / 1000) + " Kbps"; + } + return speed + " bps"; + } + + private static String getOperStatus(int status) { + switch(status) { + case 1: return "Up"; + case 2: return "Down"; + case 3: return "Testing"; + case 4: return "Unknown"; + case 5: return "Dormant"; + case 6: return "NotPresent"; + case 7: return "LowerLayerDown"; + default: return "未知"; + } + } +} \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..f6b53d6 --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,25 @@ +server: + port: 9018 + servlet: + context-path: /agent-server + +# 接口文档配置 +knife4j: + enable: true + production: false # 开启屏蔽文档资源 + +# 日志配置 +logging: + file: + path: /data/agent-server/logs + +netty: + server: + host: 127.0.0.1 + port: 6610 + client: + client-id: client-001 + reconnect-interval: 5 + maxReconnectAttempts: 10 # 最大重连次数 + initialReconnectDelay: 1000 # 初始重连延迟(毫秒) + maxReconnectDelay: 30000 # 最大重连延迟(毫秒) \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 0000000..f6b53d6 --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,25 @@ +server: + port: 9018 + servlet: + context-path: /agent-server + +# 接口文档配置 +knife4j: + enable: true + production: false # 开启屏蔽文档资源 + +# 日志配置 +logging: + file: + path: /data/agent-server/logs + +netty: + server: + host: 127.0.0.1 + port: 6610 + client: + client-id: client-001 + reconnect-interval: 5 + maxReconnectAttempts: 10 # 最大重连次数 + initialReconnectDelay: 1000 # 初始重连延迟(毫秒) + maxReconnectDelay: 30000 # 最大重连延迟(毫秒) \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..68747ab --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,31 @@ +spring: + profiles: + active: dev + mvc: + pathmatch: + matching-strategy: ant_path_matcher + application: + name: agent-server + version: 1.0 + web: + resources: + static-locations: classpath*:/META-INF/resources/ +logging: + config: classpath:logback/logback-${spring.profiles.active}.xml + +# springdoc-openapi项目配置 +knife4j: + setting: + enable-footer-custom: true + footer-custom-content: Apache License 2.0 +springdoc: + config: + title: AGENT服务 + description: AGENT服务接口文档 + contact: TCP + email: + version: ${spring.application.version} + group-configs: + - group: 'TCP服务' + paths-to-match: '/**' + packages-to-scan: com.tongran.agent-server \ No newline at end of file diff --git a/src/main/resources/logback/logback-dev.xml b/src/main/resources/logback/logback-dev.xml new file mode 100644 index 0000000..cf1e7db --- /dev/null +++ b/src/main/resources/logback/logback-dev.xml @@ -0,0 +1,116 @@ + + + + + ${appName} + + + + + + + [requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n + + + + + + + ${LOG_HOME}/debug.log + + + + ${LOG_HOME}/debug.%d{yyyy-MM-dd}.%i.AssertLog.zip + + 30 + + 10GB + + + + 10MB + + + + + + [requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n + + + + + + + + INFO + ACCEPT + DENY + + + ${LOG_HOME}/info.log + + + + ${LOG_HOME}/info.%d{yyyy-MM-dd}.%i.AssertLog.zip + + 30 + + 10GB + + + + 10MB + + + + + + [requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n + + + + + + + + ERROR + ACCEPT + DENY + + ${LOG_HOME}/error.log + + + + ${LOG_HOME}/error.%d{yyyy-MM-dd}.%i.AssertLog.zip + + 30 + + 10GB + + + + 10MB + + + + + + [requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/logback/logback-prod.xml b/src/main/resources/logback/logback-prod.xml new file mode 100644 index 0000000..859494f --- /dev/null +++ b/src/main/resources/logback/logback-prod.xml @@ -0,0 +1,115 @@ + + + + + ${appName} + + + + + + + [requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n + + + + + + + ${LOG_HOME}/debug.log + + + + ${LOG_HOME}/debug.%d{yyyy-MM-dd}.%i.AssertLog.zip + + 30 + + 10GB + + + + 10MB + + + + + + [requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n + + + + + + + + INFO + ACCEPT + DENY + + + ${LOG_HOME}/info.log + + + + ${LOG_HOME}/info.%d{yyyy-MM-dd}.%i.AssertLog.zip + + 30 + + 10GB + + + + 100MB + + + + + + [requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n + + + + + + + + ERROR + ACCEPT + DENY + + ${LOG_HOME}/error.log + + + + ${LOG_HOME}/error.%d{yyyy-MM-dd}.%i.AssertLog.zip + + 30 + + 10GB + + + + 10MB + + + + + + [requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/com/tongran/agentserver/AgentServerApplicationTests.java b/src/test/java/com/tongran/agentserver/AgentServerApplicationTests.java new file mode 100644 index 0000000..f6d2db2 --- /dev/null +++ b/src/test/java/com/tongran/agentserver/AgentServerApplicationTests.java @@ -0,0 +1,13 @@ +package com.tongran.agentserver; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AgentServerApplicationTests { + + @Test + void contextLoads() { + } + +}