oshi开源地址: github.com/oshi/oshi
因为需求要监控服务器信息,查阅了相关资料最终决定使用oshi(OSHI 是一个免费的基于 JNA(本机)的 Java 操作系统和硬件信息库)来处理。
添加依赖
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>6.1.3</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.4</version>
</dependency>
复制代码
新增浮点计算工具类Arith
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
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);
}
/** */
/**
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。
*
* @param v1 被除数
* @param v2 除数
* @return 两个参数的商
*/
public static double divWithOutZero(double v1, double v2) {
if (v1 == 0 || v2 == 0) {
return 0;
}
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));
return b1.divide(b2, scale, BigDecimal.ROUND_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, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 指定保留n位小数
*
* @param str
* @param keepType "#0.##" 假如两位
* @param keepLength ".00" 假如两位
* @return
*/
public static String formatDouble(Double str, String keepType, String keepLength) {
// 定义一个数字格式化对象,格式化模板为".#",即保留1位小数.
DecimalFormat b = new DecimalFormat(keepType);
b.applyPattern(keepLength);
b.setRoundingMode(RoundingMode.FLOOR); // 不四舍五入
String resultData = b.format(str);
return resultData;
}
/**
* 归一
*
* @param <T>
* @param map
* @return
*/
public static <T> Map<T, Double> normalized(Map<T, Double> map) {
double sum = 0;
for (Double v : map.values()) {
if (v.isInfinite() || v.isNaN()) {
v = 0.0;
}
sum += v;
}
for (T k : map.keySet()) {
Double v = map.get(k) / sum;
if (v.isInfinite() || v.isNaN()) {
v = 0.0;
}
map.put(k, v);
}
return map;
}
/**
* 归一
*
* @param <T>
* @param map
* @return
*/
public static List<Double> normalized(List<Double> all) {
double sum = 0;
for (Double v : all) {
if (v == null || v.isInfinite() || v.isNaN()) {
v = 0.0;
}
sum += v;
}
List<Double> result = new ArrayList<>();
for (Double v : all) {
result.add(v / sum);
}
return result;
}
}
复制代码
Demo演示基本用法
import cn.hutool.core.date.DateUtil;
import oshi.SystemInfo;
import oshi.hardware.*;
import oshi.software.os.FileSystem;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
/**
* @author 瓦力
* @description oshi基本用法
* @date 2022-02-23 20:49
*/
public class OshiDemo {
public static void main(String[] args) throws UnknownHostException, InterruptedException {
SystemInfo si = new SystemInfo();
OperatingSystem operatingSystem = si.getOperatingSystem();
HardwareAbstractionLayer hardware = si.getHardware();
Sensors sensors = hardware.getSensors();
CentralProcessor processor = hardware.getProcessor();
// CPU信息
printCpuInfo(processor, sensors);
// 磁盘
printSysFiles(operatingSystem);
// 运行内存
printMem(hardware.getMemory());
// 操作系统
printSystemInfo(operatingSystem);
// JVM
printJVM();
printNetWork( hardware);
}
public static void printCpuInfo(CentralProcessor processor, Sensors sensors) throws InterruptedException {
System.out.println("CPU基本信息");
long[] startTicks = processor.getSystemCpuLoadTicks();
TimeUnit.MILLISECONDS.sleep(2000);
long[] endTicks = processor.getSystemCpuLoadTicks();
long user = endTicks[CentralProcessor.TickType.USER.getIndex()] - startTicks[CentralProcessor.TickType.USER.getIndex()];
long nice = endTicks[CentralProcessor.TickType.NICE.getIndex()] - startTicks[CentralProcessor.TickType.NICE.getIndex()];
long sys = endTicks[CentralProcessor.TickType.SYSTEM.getIndex()] - startTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
long idle = endTicks[CentralProcessor.TickType.IDLE.getIndex()] - startTicks[CentralProcessor.TickType.IDLE.getIndex()];
long ioWait = endTicks[CentralProcessor.TickType.IOWAIT.getIndex()] - startTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
long irq = endTicks[CentralProcessor.TickType.IRQ.getIndex()] - startTicks[CentralProcessor.TickType.IRQ.getIndex()];
long softIrq = endTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - startTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
long steal = endTicks[CentralProcessor.TickType.STEAL.getIndex()] - startTicks[CentralProcessor.TickType.STEAL.getIndex()];
long totalCpu = user + nice + sys + idle + ioWait + irq + softIrq + steal;
// CPU 温度(以摄氏度为单位)(如果有)
System.out.println(String.format("CPU型号:%s\tCPU核心数:%d\tCPU温度:%s\t总使用率:%s\t系统使用率:%s\t当前空闲率:%s\t用户使用率:%s",
processor.getProcessorIdentifier().getName(),
processor.getLogicalProcessorCount(),
sensors.getCpuTemperature(),
Arith.round(Arith.mul(totalCpu, 100), 2),
Arith.round(Arith.mul(Arith.div(sys, totalCpu, 2), 100), 2),
Arith.round(Arith.mul(Arith.div(idle, totalCpu, 2), 100), 2),
Arith.round(Arith.mul(Arith.div(user, totalCpu, 2), 100), 2)
)
);
}
public static void printSysFiles(OperatingSystem os) {
System.out.println("磁盘内存");
FileSystem fileSystem = os.getFileSystem();
List<OSFileStore> fileStores = fileSystem.getFileStores();
for (OSFileStore fs : fileStores) {
long free = fs.getUsableSpace();
long total = fs.getTotalSpace();
long used = total - free;
System.out.println(String.format("盘符:%s\t类型:%s\t盘名:%s\t总量:%s\t剩余:%s\t使用:%s\t使用率:%s", fs.getMount(), fs.getType(), fs.getName()
, convertFileSize(total), convertFileSize(free), convertFileSize(used), Arith.mul(Arith.div(used, total, 2), 100)));
}
}
private static void printSystemInfo(OperatingSystem operatingSystem) throws UnknownHostException {
System.out.println("系统信息");
OperatingSystem.OSVersionInfo versionInfo = operatingSystem.getVersionInfo();
InetAddress ip = Inet4Address.getLocalHost();
Properties properties = System.getProperties();
System.out.println("主机名:" + ip.getHostName());
/* 厂家 + 家族 + 版本 + 构建版号*/
System.out.println("系统描述:" + operatingSystem.getManufacturer() + " " + operatingSystem.getFamily() + versionInfo.getVersion() + " Build " + versionInfo.getBuildNumber());
System.out.println("进程运行数量:" + operatingSystem.getProcessCount());
System.out.println("线程运行数量:" + operatingSystem.getThreadCount());
System.out.println("系统位数:" + properties.getProperty("os.arch"));
System.out.println("系统版本:" + properties.getProperty("os.version"));
System.out.println("系统部署目录:" + properties.getProperty("user.dir"));
System.out.println("系统IP:" + ip.getHostAddress());
System.out.println("系统支持位数" + operatingSystem.getBitness());
/**
* 系统启动的大致时间,以秒为单位。
*/
Date systemBootTime = new Date(operatingSystem.getSystemBootTime() * 1000);
System.out.println("系统启动时间:" + DateUtil.format(systemBootTime, "yyyy-MM-dd HH:mm:ss"));
/**
* 自启动以来的秒数,运行时长
*/
System.out.println("运行时长:" + secondToTime(operatingSystem.getSystemUptime()));
}
public static void printMem(GlobalMemory m) {
System.out.println("内存信息");
System.out.println("总内存:" + convertFileSize(m.getTotal()));
System.out.println("已用内存:" + convertFileSize(m.getTotal() - m.getAvailable()));
System.out.println("剩余内存:" + convertFileSize(m.getAvailable()));
}
public static void printJVM() {
System.out.println("JVM");
Properties props = System.getProperties();
// 当前可用的内存总量MB
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("JVM当前可用的内存总量:" + convertFileSize(totalMemory) );
// 当前内存总量的近似值
long freeMemory = Runtime.getRuntime().freeMemory();
System.out.println("JVM当前内存总量的近似值:" + convertFileSize(freeMemory ));
// 虚拟机的最大内存容量
long maxMemory = Runtime.getRuntime().maxMemory();
System.out.println("JVM最大内存容量" + convertFileSize(maxMemory) );
System.out.println("版本:" + props.getProperty("java.version"));
System.out.println("JAVA_HOME:" + props.getProperty("java.home"));
}
public static void printNetWork(HardwareAbstractionLayer hardware) {
System.out.println("网络带宽");
List<NetworkIF> networkIFs = hardware.getNetworkIFs();
for (NetworkIF networkIF : networkIFs) {
System.out.println(String.format("IPV4:%s\t网络接收:%s\t网络发送:%s\t显示名称:%s\tMAC地址:%s\t",
Arrays.toString(networkIF.getIPv4addr()),
networkIF.getBytesRecv(),
networkIF.getBytesSent(),
networkIF.getDisplayName(),
networkIF.getMacaddr()));
}
}
/**
* 字节转换
*
* @param size 字节大小
* @return 转换后值
*/
public static 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);
}
}
/**
* 将秒数转换为日时分秒
*
* @param second
* @return
*/
public static String secondToTime(long second) {
long days = second / 86400; //转换天数
second = second % 86400; //剩余秒数
long hours = second / 3600; //转换小时
second = second % 3600; //剩余秒数
long minutes = second / 60; //转换分钟
second = second % 60; //剩余秒数
if (days > 0) {
return days + "天" + hours + "小时" + minutes + "分" + second + "秒";
} else {
return hours + "小时" + minutes + "分" + second + "秒";
}
}
}
复制代码
运行结果截图
注意事项
该demo使用的springboot版本是2.3.1.RELEASE