前言
我们在常规性系统观测领域会做一些系统层面的服务指标暂时仪表盘,但基本上对系统的指标或许、进程的指标获取用java其实也有点难度的,今天我们学一下OSHI这个类库,它提供了很多方法用于获取系统和进程数据,咱们来做个漂亮的仪表盘。
OSHI
OSHI是Java的免费的基于JNA的(本机)操作系统和硬件信息库。它不需要安装任何其他本机库,并且旨在提供一种跨平台的实现来检索系统信息,例如OS版本,进程,内存和CPU使用率,磁盘和分区,设备,传感器等。
支持平台:
Windows • Linux • macOS • Unix (AIX, FreeBSD, Solaris)
– 计算机系统和固件,底板
– 操作系统和版本/内部版本
– 物理(核心)和逻辑(超线程)CPU,处理器组,NUMA节点
– 系统和每个处理器的负载,使用情况滴答计数器,中断,正常运行时间
– 进程正常运行时间,CPU,内存使用情况,用户/组,命令行参数,线程详细信息
– 已使用/可用的物理和虚拟内存
– 挂载的文件系统(类型,可用空间和总空间,选项,读取和写入)
– 磁盘驱动器(型号,序列号,大小,读取和写入)和分区
– 网络接口(IP,带宽输入/输出),网络参数,TCP / UDP统计信息
– 电池状态(电量百分比,剩余时间,电量使用情况统计信息)
– USB设备
– 连接的显示器(带有EDID信息),图形和声卡
– 某些硬件上的传感器(温度,风扇速度,电压)
所以绝大部分平台都满足需求了,我们来看看他的一些获取系统指标的方法,有很多我也没整明白,但是大部分看懂的已经够用了。
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>6.2.2</version>
</dependency>
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.List;
import java.util.Properties;
/**
* @author cookie.joo
* @date 2022-08-25
*/
public class OshiTest {
public void init() 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(1000);
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;
// 核心数 processor.getLogicalProcessorCount()
// CPU总的使用率 totalCpu
// CPU系统使用率 cSys
// CPU用户使用率 user
// CPU当前等待率 iowait
// CPU当前空闲率 idle
}
/**
* 设置内存信息
*/
private void setMemInfo(GlobalMemory memory) {
// 内存总量 memory.getTotal()
// 已用内存 memory.getTotal() - memory.getAvailable()
// 剩余内存 memory.getAvailable()
}
/**
* 设置服务器信息
*/
private void setSysInfo() {
Properties props = System.getProperties();
// 操作系统 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();
// 内存总量 Runtime.getRuntime().totalMemory()
// 当前内存使用率 Runtime.getRuntime().maxMemory()
// 内存空闲率 Runtime.getRuntime().freeMemory()
// JDK版本 props.getProperty("java.version")
// JDK安装路径 props.getProperty("java.home")
}
/**
* 设置磁盘信息
*/
private void setSysFiles(OperatingSystem os) {
FileSystem fileSystem = os.getFileSystem();
List<OSFileStore> fsArray = fileSystem.getFileStores();
for (OSFileStore fs : fsArray) {
long free = fs.getUsableSpace();
long total = fs.getTotalSpace();
long used = total - free;
// 盘符路径 fs.getMount()
// 盘符类型 fs.getType()
// 文件类型 fs.getName());
// 总大小 convertFileSize(total)
// 剩余大小 convertFileSize(free)
// 已经使用量 sysFile.setUsed(convertFileSize(used)
// 资源的使用率 Arith.mul(Arith.div(used, total, 4), 100)
}
}
/**
* 字节转换
*
* @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);
}
}
}
仪表盘
我们借用开源的一个界面来做参考,叫WARD的一个GitHub项目,有兴趣可以去看看,其实就是用的OSHI,前端用的js和css生成的动态背景图很酷炫漂亮,我把他集成到我的个人web项目里面偶尔可以打开看看机器负载情况。
建立一个Controller类前端每个一秒钟来获取一下系统指标数据即可。重点要说一下这个Util.sleep(1000)为什么要休眠一会,然后用当前时间值减去上一个时间值,我CentralProcessor.getSystemCpuLoadTicks方法注释上看到了说明,意思是通过测量跨时间间隔的节拍之间的差异,可以计算该时间间隔上的CPU负载。它目的是计算当前直接的使用率,这个值是不停的递增的,所以必须用这个方式来得到这个值。
private int getProcessor(){
CentralProcessor centralProcessor = systemInfo.getHardware().getProcessor();
// 第一次获取:获取系统范围的 CPU 负载滴答计数器:
long[] prevTicksArray = centralProcessor.getSystemCpuLoadTicks();
long prevTotalTicks = Arrays.stream(prevTicksArray).sum();
long prevIdleTicks = prevTicksArray[CentralProcessor.TickType.IDLE.getIndex()];
//等待一段时间
Util.sleep(1000);
//第二次获取:获取系统范围的 CPU 负载滴答计数器:
long[] currTicksArray = centralProcessor.getSystemCpuLoadTicks();
long currTotalTicks = Arrays.stream(currTicksArray).sum();
long currIdleTicks = currTicksArray[CentralProcessor.TickType.IDLE.getIndex()];
return (int) Math.round((1 - ((double) (currIdleTicks - prevIdleTicks)) / ((double) (currTotalTicks - prevTotalTicks))) * 100);
}
最后
OSHI的工具类让我们在Java中获取系统参数指标提供了遍历,WARD项目让我们在开发web应用的时候多了一个赏心悦目的仪表盘,让我们非前端开发人员也能做出一个漂亮的前端展示来,这个前端在手机端浏览也是适配的。
可以点击我的服务链接看这个前端显示效果。
OSHI更多参数请到官网中探索吧!