关注程序员的故事,一起用技术改变世界

OSHI介绍

OSHI是一个基于JNA的免费的本地操作系统和Java的硬件信息库。它不需要安装任何额外的本机库,旨在提供跨平台的实现来检索系统信息,如操作系统版本、进程、内存和CPU使用情况、磁盘和分区、设备、传感器等。

支持的功能

  • 计算机系统和固件,底板

  • 操作系统和版本/内部版本

  • 物理(核心)和逻辑(超线程)CPU,处理器组,NUMA节点

  • 系统和每个处理器的负载,使用情况滴答计数器,中断,正常运行时间

  • 进程正常运行时间,CPU,内存使用情况,用户/组,命令行参数,线程详细信息

  • 已使用/可用的物理和虚拟内存

  • 挂载的文件系统(类型,可用空间和总空间,选项,读取和写入)

  • 磁盘驱动器(型号,序列号,大小,读取和写入)和分区

  • 网络接口(IP,带宽输入/输出),网络参数,TCP / UDP统计信息

  • 电池状态(电量百分比,剩余时间,电量使用情况统计信息)

  • USB设备

  • 连接的显示器(带有EDID信息),图形和声卡

  • 某些硬件上的传感器(温度,风扇速度,电压)

使用说明

第一步:SpringBoot项目添加依赖:

com.github.oshigroupId> oshi-coreartifactId> 3.9.1version> dependency>

第二步:创建CPU,虚拟机,内存,系统,系统文件等系统基础类:

CPU:

package com.company.api.server;
import com.company.util.Arith;
/** * CPU相关信息 * @author zengxueqi * @since 2020/07/14 */ public class Cpu { /** * 核心数 */ private int cpuNum;
/** * CPU总的使用率 */ private double total;
/** * CPU系统使用率 */ private double sys;
/** * CPU用户使用率 */ private double used;
/** * CPU当前等待率 */ private double wait;
/** * CPU当前空闲率 */ private double free;
public int getCpuNum() { return cpuNum; }
public void setCpuNum(int cpuNum) { this.cpuNum = cpuNum; }
public double getTotal() { return Arith.round(Arith.mul(total, 100), 2); }
public void setTotal(double total) { this.total = total; }
public double getSys() { return Arith.round(Arith.mul(sys / total, 100), 2); }
public void setSys(double sys) { this.sys = sys; }
public double getUsed() { return Arith.round(Arith.mul(used / total, 100), 2); }
public void setUsed(double used) { this.used = used; }
public double getWait() { return Arith.round(Arith.mul(wait / total, 100), 2); }
public void setWait(double wait) { this.wait = wait; }
public double getFree() { return Arith.round(Arith.mul(free / total, 100), 2); }
public void setFree(double free) { this.free = free; }
}

虚拟机

package com.company.api.server;
import com.company.util.Arith; import com.company.util.util.DateUtils;
import java.lang.management.ManagementFactory;
/** * JVM相关信息 * @author zengxueqi * @since 2020/07/14 */ public class Jvm {
/** * 当前JVM占用的内存总数(M) */ private double total;
/** * JVM最大可用内存总数(M) */ private double max;
/** * JVM空闲内存(M) */ private double free;
/** * JDK版本 */ private String version;
/** * JDK路径 */ private String home;
public double getTotal() { return Arith.div(total, (1024 * 1024), 2); }
public void setTotal(double total) { this.total = total; }
public double getMax() { return Arith.div(max, (1024 * 1024), 2); }
public void setMax(double max) { this.max = max; }
public double getFree() { return Arith.div(free, (1024 * 1024), 2); }
public void setFree(double free) { this.free = free; }
public double getUsed() { return Arith.div(total - free, (1024 * 1024), 2); }
public double getUsage() { return Arith.mul(Arith.div(total - free, total, 4), 100); }
/** * 获取JDK名称 */ public String getName() { return ManagementFactory.getRuntimeMXBean().getVmName(); }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public String getHome() { return home; }
public void setHome(String home) { this.home = home; }
/** * JDK启动时间 */ public String getStartTime() { return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()); }
/** * JDK运行时间 */ public String getRunTime() { return DateUtils.getDatePoor(DateUtils.getNowDate(), DateUtils.getServerStartDate()); }
}

内存:

package com.company.api.server;
import com.company.util.Arith;
/** * 內存相关信息 * @author zengxueqi * @since 2020/07/14 */ public class Mem {
/** * 内存总量 */ private double total;
/** * 已用内存 */ private double used;
/** * 剩余内存 */ private double free;
public double getTotal() { return Arith.div(total, (1024 * 1024 * 1024), 2); }
public void setTotal(long total) { this.total = total; }
public double getUsed() { return Arith.div(used, (1024 * 1024 * 1024), 2); }
public void setUsed(long used) { this.used = used; }
public double getFree() { return Arith.div(free, (1024 * 1024 * 1024), 2); }
public void setFree(long free) { this.free = free; }
public double getUsage() { return Arith.mul(Arith.div(used, total, 4), 100); }
}

系统:

package com.company.api.server;
/** * 系统相关信息 * @author zengxueqi * @since 2020/07/14 */ public class Sys {
/** * 服务器名称 */ private String computerName;
/** * 服务器Ip */ private String computerIp;
/** * 项目路径 */ private String userDir;
/** * 操作系统 */ private String osName;
/** * 系统架构 */ private String osArch;
public String getComputerName() { return computerName; }
public void setComputerName(String computerName) { this.computerName = computerName; }
public String getComputerIp() { return computerIp; }
public void setComputerIp(String computerIp) { this.computerIp = computerIp; }
public String getUserDir() { return userDir; }
public void setUserDir(String userDir) { this.userDir = userDir; }
public String getOsName() { return osName; }
public void setOsName(String osName) { this.osName = osName; }
public String getOsArch() { return osArch; }
public void setOsArch(String osArch) { this.osArch = osArch; } }

系统文件:

package com.company.api.server;
/** * 系统文件相关信息 * @author zengxueqi * @since 2020/07/14 */ public class SysFile {
/** * 盘符路径 */ private String dirName;
/** * 盘符类型 */ private String sysTypeName;
/** * 文件类型 */ private String typeName;
/** * 总大小 */ private String total;
/** * 剩余大小 */ private String free;
/** * 已经使用量 */ private String used;
/** * 资源的使用率 */ private double usage;
public String getDirName() { return dirName; }
public void setDirName(String dirName) { this.dirName = dirName; }
public String getSysTypeName() { return sysTypeName; }
public void setSysTypeName(String sysTypeName) { this.sysTypeName = sysTypeName; }
public String getTypeName() { return typeName; }
public void setTypeName(String typeName) { this.typeName = typeName; }
public String getTotal() { return total; }
public void setTotal(String total) { this.total = total; }
public String getFree() { return free; }
public void setFree(String free) { this.free = free; }
public String getUsed() { return used; }
public void setUsed(String used) { this.used = used; }
public double getUsage() { return usage; }
public void setUsage(double usage) { this.usage = usage; }
}

封装服务器监控系统:

package com.company.api.server;
import com.company.util.Arith; import com.company.util.util.IpUtils; import oshi.SystemInfo; import oshi.hardware.CentralProcessor; import oshi.hardware.CentralProcessor.TickType; import oshi.hardware.GlobalMemory; import oshi.hardware.HardwareAbstractionLayer; import oshi.software.os.FileSystem; import oshi.software.os.OSFileStore; import oshi.software.os.OperatingSystem; import oshi.util.Util;
import java.net.UnknownHostException; import java.util.LinkedList; import java.util.List; import java.util.Properties;
/** * 服务器相关信息 * @author zengxueqi * @since 2020/07/14 */ public class Server {
private static final int OSHI_WAIT_SECOND = 1000;
/** * CPU相关信息 */ private Cpu cpu = new Cpu();
/** * 內存相关信息 */ private Mem mem = new Mem();
/** * JVM相关信息 */ private Jvm jvm = new Jvm();
/** * 服务器相关信息 */ private Sys sys = new Sys();
/** * 磁盘相关信息 */ private List sysFiles = new LinkedList();
public Cpu getCpu() { return cpu; }
public void setCpu(Cpu cpu) { this.cpu = cpu; }
public Mem getMem() { return mem; }
public void setMem(Mem mem) { this.mem = mem; }
public Jvm getJvm() { return jvm; }
public void setJvm(Jvm jvm) { this.jvm = jvm; }
public Sys getSys() { return sys; }
public void setSys(Sys sys) { this.sys = sys; }
public List getSysFiles() { return sysFiles; }
public void setSysFiles(List sysFiles) { this.sysFiles = sysFiles; }
public void copyTo() throws Exception { SystemInfo si = new SystemInfo(); HardwareAbstractionLayer hal = si.getHardware(); setCpuInfo(hal.getProcessor()); setMemInfo(hal.getMemory()); setSysInfo(); setJvmInfo(); setSysFiles(si.getOperatingSystem()); }
/** * 设置CPU信息 */ private void setCpuInfo(CentralProcessor processor) { // CPU信息 long[] prevTicks = processor.getSystemCpuLoadTicks(); Util.sleep(OSHI_WAIT_SECOND); long[] ticks = processor.getSystemCpuLoadTicks(); long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; cpu.setCpuNum(processor.getLogicalProcessorCount()); cpu.setTotal(totalCpu); cpu.setSys(cSys); cpu.setUsed(user); cpu.setWait(iowait); cpu.setFree(idle); }
/** * 设置内存信息 */ private void setMemInfo(GlobalMemory memory) { mem.setTotal(memory.getTotal()); mem.setUsed(memory.getTotal() - memory.getAvailable()); mem.setFree(memory.getAvailable()); }
/** * 设置服务器信息 */ private void setSysInfo() { Properties props = System.getProperties(); sys.setComputerName(IpUtils.getHostName()); sys.setComputerIp(IpUtils.getHostIp()); sys.setOsName(props.getProperty("os.name")); sys.setOsArch(props.getProperty("os.arch")); sys.setUserDir(props.getProperty("user.dir")); }
/** * 设置Java虚拟机 */ private void setJvmInfo() throws UnknownHostException { Properties props = System.getProperties(); jvm.setTotal(Runtime.getRuntime().totalMemory()); jvm.setMax(Runtime.getRuntime().maxMemory()); jvm.setFree(Runtime.getRuntime().freeMemory()); jvm.setVersion(props.getProperty("java.version")); jvm.setHome(props.getProperty("java.home")); }
/** * 设置磁盘信息 */ private void setSysFiles(OperatingSystem os) { FileSystem fileSystem = os.getFileSystem(); OSFileStore[] fsArray = fileSystem.getFileStores(); for (OSFileStore fs : fsArray) { long free = fs.getUsableSpace(); long total = fs.getTotalSpace(); long used = total - free; SysFile sysFile = new SysFile(); sysFile.setDirName(fs.getMount()); sysFile.setSysTypeName(fs.getType()); sysFile.setTypeName(fs.getName()); sysFile.setTotal(convertFileSize(total)); sysFile.setFree(convertFileSize(free)); sysFile.setUsed(convertFileSize(used)); sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); sysFiles.add(sysFile); } }
/** * 字节转换 * @param size 字节大小 * @return 转换后值 */ public String convertFileSize(long size) { long kb = 1024; long mb = kb * 1024; long gb = mb * 1024; if (size >= gb) { return String.format("%.1f GB", (float) size / gb); } else if (size >= mb) { float f = (float) size / mb; return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f); } else if (size >= kb) { float f = (float) size / kb; return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f); } else { return String.format("%d B", size); } }
}

创建浮点数运算工具类:

package com.company.util;
import java.math.BigDecimal; import java.math.RoundingMode;
/** * 精确的浮点数运算 * @author zengxueqi * @since 2020/07/14 */ public class Arith {
/** * 默认除法运算精度 */ private static final int DEF_DIV_SCALE = 10;
/** * 这个类不能实例化 */ private Arith() { }
/** * 提供精确的加法运算。 * @param v1 被加数 * @param v2 加数 * @return 两个参数的和 */ public static double add(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.add(b2).doubleValue(); }
/** * 提供精确的减法运算。 * @param v1 被减数 * @param v2 减数 * @return 两个参数的差 */ public static double sub(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.subtract(b2).doubleValue(); }
/** * 提供精确的乘法运算。 * @param v1 被乘数 * @param v2 乘数 * @return 两个参数的积 */ public static double mul(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.multiply(b2).doubleValue(); }
/** * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 * 小数点以后10位,以后的数字四舍五入。 * @param v1 被除数 * @param v2 除数 * @return 两个参数的商 */ public static double div(double v1, double v2) { return div(v1, v2, DEF_DIV_SCALE); }
/** * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 * 定精度,以后的数字四舍五入。 * @param v1 被除数 * @param v2 除数 * @param scale 表示表示需要精确到小数点以后几位。 * @return 两个参数的商 */ public static double div(double v1, double v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); if (b1.compareTo(BigDecimal.ZERO) == 0) { return BigDecimal.ZERO.doubleValue(); } return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue(); }
/** * 提供精确的小数位四舍五入处理。 * @param v 需要四舍五入的数字 * @param scale 小数点后保留几位 * @return 四舍五入后的结果 */ public static double round(double v, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b = new BigDecimal(Double.toString(v)); BigDecimal one = new BigDecimal("1"); return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue(); } }

第三步:创建api接口:

package com.company.mission.controller;
import com.company.api.base.ResultT; import com.company.api.server.Server; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
/** * 服务器监控 * @author zengxueqi * @since 2020/07/14 */ @RestController @RequestMapping("/monitor") public class ServerController {
/** * 获取服务器监控信息 * @param * @return com.company.api.base.ResultT * @author zengxueqi * @since 2020/7/14 */ @PostMapping("/server") public ResultT getInfo() throws Exception { Server server = new Server(); server.copyTo(); return ResultT.ok(server); }
}

第四步:启动SpringBoot服务,访问API地址:

API地址(根据自己的项目来):http://127.0.0.1:9004/mission/monitor/server

API接口响应数据如下:

{ "code": 0, "msg": "业务处理成功", "content": { "cpu": { "cpuNum": 8, "total": 80300.0, "sys": 4.61, "used": 13.45, "wait": 0.0, "free": 81.94 }, "mem": { "total": 8.0, "used": 6.22, "free": 1.78, "usage": 77.71 }, "jvm": { "total": 656.0, "max": 1820.5, "free": 161.44, "version": "1.8.0_191", "home": "/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre", "name": "Java HotSpot(TM) 64-Bit Server VM", "startTime": "2020-07-14 10:30:46", "usage": 75.39, "used": 494.56, "runTime": "0天1小时5分钟" }, "sys": { "computerName": "zengxueqis-MacBook-Air.local", "computerIp": "127.0.0.1", "userDir": "/Users/zengxueqi/IdeaProjects/mission", "osName": "Mac OS X", "osArch": "x86_64" }, "sysFiles": [ { "dirName": "/", "sysTypeName": "apfs", "typeName": "mac os", "total": "79.4 GB", "free": "24.0 GB", "used": "55.4 GB", "usage": 69.8 }, { "dirName": "/System/Volumes/Data", "sysTypeName": "apfs", "typeName": "mac os - 数据", "total": "79.4 GB", "free": "24.0 GB", "used": "55.4 GB", "usage": 69.8 }, { "dirName": "/private/var/vm", "sysTypeName": "apfs", "typeName": "VM", "total": "79.4 GB", "free": "24.0 GB", "used": "55.4 GB", "usage": 69.8 }, { "dirName": "/Volumes/Untitled", "sysTypeName": "ntfs", "typeName": "Untitled", "total": "38.3 GB", "free": "17.2 GB", "used": "21.0 GB", "usage": 54.94 }, { "dirName": "/Volumes/Untitled 1", "sysTypeName": "ntfs", "typeName": "Untitled 1", "total": "38.7 GB", "free": "19.7 GB", "used": "19.0 GB", "usage": 48.97 }, { "dirName": "/Volumes/软件", "sysTypeName": "ntfs", "typeName": "软件", "total": "171.0 GB", "free": "163.4 GB", "used": "7.6 GB", "usage": 4.46 }, { "dirName": "/Volumes/other", "sysTypeName": "apfs", "typeName": "other", "total": "170.1 GB", "free": "164.6 GB", "used": "5.5 GB", "usage": 3.22 }, { "dirName": "/Volumes/文档", "sysTypeName": "ntfs", "typeName": "文档", "total": "170.0 GB", "free": "166.9 GB", "used": "3.1 GB", "usage": 1.81 }, { "dirName": "/Volumes/娱乐", "sysTypeName": "ntfs", "typeName": "娱乐", "total": "170.0 GB", "free": "159.8 GB", "used": "10.2 GB", "usage": 5.98 }, { "dirName": "/Volumes/办公", "sysTypeName": "ntfs", "typeName": "办公", "total": "170.0 GB", "free": "168.9 GB", "used": "1.1 GB", "usage": 0.63 } ] } }

通过API接口返回的数据可以看出,OSHI获取到的服务器数据还是非常的详细的,OSHI可以跨平台查看服务器信息,其中cpu负载信息为当前占用CPU的时间。需要在一段时间内获取两次,然后相减得出这段时间内所占用的时间。这段时间除以总占用时间就是占用百分比。