Linux下Java獲取CPU、内存、磁盤IO、网络相應信息
這個話題不是一個很新的東西,很多工具都做了這方面的工作。出於個人興趣,我就實作一個小工具,實現了:
- CPU【user】【sys 】【iowait】的動態讀取
- Network【Receice】【Transmit】的動態讀取
- Disk【rkB/s】【wkB/s】的動態讀取
- Memeory【内存使用率】的動態分析
模仿的對象
參考數據PDFLinux System and Performance Monitoring
開發環境:大部分代碼我是在win10下MyEclipse下進行語法檢查的,細緻的編寫是在CentOS 7下gedit,編譯則是在Terminal(虛擬機下的CentOS 7實在是承受不了MyEclipse(太耗CPU和内存),本身在Terminal 下打開java程序就很慢。)。
其實gedit也是一個非常優秀的Coding Tool。
CentOS 7版本:
[root@localhost ~]# uname -a
Linux localhost.localdomain 3.10.0-229.el7.x86_64 #1 SMP Fri Mar 6 11:36:42 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
JDK版本:(果斷放棄自帶的OpenJDK)
[root@localhost ~]# java -version
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
1. 原理
下面是我的思考過程:
如果單單是利用Linux下的指令,很容易獲取相應的信息,如:使用iostat -c查看CPU的使用率情况。
備注:如果這個指令找不到,需要先安裝sysstat。我在rhl5上測試這條指令時就提示無法找到這條指令。當然超簡單的。
[root@localhost class]# iostat -c
Linux 3.10.0-229.el7.x86_64 (localhost.localdomain) 2016年12月25日 _x86_64_ (2 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
14.17 0.21 2.09 0.38 0.00 83.15
现在想用java来获取?怎么写呢?之前我想,在linux環境下,用管道把指令獲取的信息保存在TXT文件裏,用java去讀取文件的内容,不就可以實現抓取linux系統的相應信息。
[root@localhost ~]# iostat -c -> cpu.txt
[root@localhost ~]# cat cpu.txt
Linux 3.10.0-229.el7.x86_64 (localhost.localdomain) 2016年12月25日 _x86_64_(2 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
11.56 0.16 1.73 0.31 0.00 86.24
但是這樣太複雜了,頻繁的創建文件讀文件也顯得繁瑣,有沒有一種直接的方法呢?上網咯查了知道:
java中調用Linux的shell命令使用使用Process和Runtime
jdk1.6 API doc:
public class Runtime extends Object
每个 Java 应用程序都有一个 Runtime 类實例,使应用程序能够与其运行的環境相连接。可以通過getRuntime 方法獲取当前运行时。 應用程序不能创建自己的 Runtime类实例。
public abstract class Process extends Object
ProcessBuilder.start() 和Runtime.exec 方法创建一个本机进程,并返回 Process子类的一个實例,该實例可用来控制進程并获得相关信息。
像這樣:
String command = "iostat -c";
Runtime r = Runtime.getRuntime();
Process pro = r.exec(command);
BufferedReader in = new BufferedReader(new InputStreamReader(pro.getInputStream()));
儅我們拿到一個BufferedReader,就可以一行一行的提取我們想要的信息。那麽問題來了,怎麽畫成圖標呢?難不成還要java自帶的類庫【大腦迅速搜索相關的類庫】畫?
這裏我們可以用JFree Chart。
什麽是JFree Chart?以下是引自百度。
JFreeChart是JAVA平台上的一个开放的图表绘制类库。它完全使用JAVA语言编写,是为applications, applets,servlets 以及JSP等使用所设计。JFreeChart可生成饼图(pie charts)、柱状图(bar charts)、散点图(scatter plots)、时序图(time series)、甘特图(Gantt charts)等等多种图表,并且可以产生PNG和JPEG格式的输出,还可以与PDF和EXCEL关联。
JFreeChart舉例:
TimeSeries timeseries1 = new TimeSeries("user%");
TimeSeries timeseries2 = new TimeSeries("sys%");
TimeSeries timeseries3 = new TimeSeries("iowait%");
TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
timeseriescollection.addSeries(timeseries1);
timeseriescollection.addSeries(timeseries2);
timeseriescollection.addSeries(timeseries3);
JFreeChart jfreechart = ChartFactory.createTimeSeriesChart("CPU Total Usage","Time(s)","Value(%)", timeseriescollection,true,true,false);
jfreechart.setBackgroundPaint(Color.white);
XYPlot xyplot = jfreechart.getXYPlot();
ValueAxis valueaxis = xyplot.getDomainAxis();
valueaxis.setAutoRange(true);
valueaxis.setFixedAutoRange(60000D);
ChartPanel CPUframe = new ChartPanel(jfreechart,true);
2.準備-通用類
這是一個通過linux下的指令,拿到指令内容的BufferedReader 的類。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.FileInputStream;
/*
* function:get the info<BufferedReader> by using Linux command
*/
public class LinuxCommand {
private Process pro = null;
private String command = null;
public LinuxCommand(String command) {
this.command = command;
}
public BufferedReader getInfoReader() {
try {
Runtime r = Runtime.getRuntime();
pro = r.exec(command);
return new BufferedReader(new InputStreamReader(pro.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public BufferedReader getInfoReader2() {
try {
return new BufferedReader(new InputStreamReader(new FileInputStream(command)));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public void processDestroy() {
if(pro != null)
pro.destroy();
}
}
3. CPU
這個類的功能是返回一個TimeSeriesChart的圖標的JPanel,同時本生是實現了Runnable接口。自身起了一個綫程,每隔一秒讀取一次“iostat -c”指令獲取【user】【sys 】【iowait】這三個信息。
* user% :在用户级别运行所使用 CPU 的百分比。
* sys% :在系统级别(kernel)运行所使用 CPU 的百分比。
* iowait% :CPU等待硬件 I/O 时,所占用 CPU 百分比。
具體代碼:
import java.awt.Color;
import java.io.BufferedReader;
import java.io.IOException;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
public class CPUTimeSeries implements Runnable{
ChartPanel CPUframe;
private TimeSeries timeseries1;
private TimeSeries timeseries2;
private TimeSeries timeseries3;
static Thread thread;
private double value1 ,value2, value3;
private static String command = "iostat -c";
private LinuxCommand linuxcommand = null;
private BufferedReader in = null;
public CPUTimeSeries(){
linuxcommand = new LinuxCommand(command);
thread = new Thread(this);
XYDataset xydataset = createDataset();
JFreeChart jfreechart = ChartFactory.createTimeSeriesChart("CPU Total Usage","Time(s)","Value(%)",
xydataset,true,true,false);
jfreechart.setBackgroundPaint(Color.white);
XYPlot xyplot = jfreechart.getXYPlot();
ValueAxis valueaxis = xyplot.getDomainAxis();
valueaxis.setAutoRange(true);
valueaxis.setFixedAutoRange(60000D);
CPUframe = new ChartPanel(jfreechart,true);
}
//produce DataSet
private XYDataset createDataset() {
timeseries1 = new TimeSeries("user%");
timeseries2 = new TimeSeries("sys%");
timeseries3 = new TimeSeries("iowait%");
TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
timeseriescollection.addSeries(timeseries1);
timeseriescollection.addSeries(timeseries2);
timeseriescollection.addSeries(timeseries3);
return timeseriescollection;
}
//return a new Panel
public ChartPanel getChartPanel(){
return CPUframe;
}
public void startThread(){
thread.start();
}
public void clearDataSet() {
this.timeseries1.clear();
this.timeseries2.clear();
this.timeseries3.clear();
}
@Override
public void run() {
while(true){
try {
in = linuxcommand.getInfoReader();
String line = null;
int cnt = 0;
while((line=in.readLine()) != null && ++cnt != 4){
}
line = line.trim();
String[] temp = line.split("\\s+");
//string to double
value1 = Double.parseDouble(temp[0]);
value2 = Double.parseDouble(temp[2]);
value3 = Double.parseDouble(temp[3]);
//fufill the timeseries
try {
Millisecond millisecond1 = new Millisecond();
timeseries1.add(millisecond1, value1);
timeseries2.add(millisecond1, value2);
timeseries3.add(millisecond1, value3);
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
in.close();
linuxcommand.processDestroy();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 結果截圖
4. Network
這個類的功能是返回一個XYAreaChart, 自身起一個綫程每秒讀取指令/proc/net/dev
指令下的【Receice】【Transmit】數據。
[root@localhost class]# cat /proc/net/dev
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
eno16777736: 303041758 228598 0 0 0 0 0 0 4846830 58293 0 0 0 0 0 0
lo: 25702 266 0 0 0 0 0 0 25702 266 0 0 0 0 0 0
關鍵代碼(重寫的run()方法):
由於【Receice】【Transmit】是一個纍積數據,所以我們想要獲取一秒内的數據傳輸量,data = new - old
,同樣的道理,如果想要獲取網口傳輸速度,衹要把時間t弄得短一些,用data / t
就是網口傳輸速度。
long startTime = System.currentTimeMillis();
long endTime = System.currentTimeMillis();
t = startTime - endTime ;
有把這三個數據放在一張表中,如果這樣的話,就有兩個坐標Y軸(單位不統一),複雜了很多,之後的版本想做一下,這個版本就簡單的實現對R&T的動態讀取,應該值得注意的是R&T的單位是byte。
@Override
public void run() {
while(true) {
in = linuxcommand.getInfoReader2();
String line = null;
try {
line = in.readLine();
line = in.readLine();
line = in.readLine();//the data we looking for
in.close();
String[] temp = line.split("[\\s]+");
rece2 = Long.parseLong(temp[1]);
tran2 = Long.parseLong(temp[9]);
} catch (IOException e2) {
e2.printStackTrace();
}
value1 = (rece2 - rece1) * 8.0D / 1024;
value2 = (tran2 - tran1) * 8.0D / 1024;
rece1 = rece2;
tran1 = tran2;
Millisecond millisecond = new Millisecond();
timeseries1.add(millisecond, value1);
timeseries2.add(millisecond, value2);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
5. Disk
這個類是獲取磁盤IO每秒讀寫的數據量,用到JFreeChart裏的TimeSeriesChart, 道理也和之前的兩個模塊的差不多,不同的是,有多個設備,需要根據用戶的選擇提取相應的設備信息。
[root@localhost class]# iostat -k -d -x
Linux 3.10.0-229.el7.x86_64 (localhost.localdomain) 2016年12月26日 _x86_64_ (2 CPU)
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.05 0.60 2.42 0.84 124.89 57.36 112.00 0.14 42.80 16.44 118.90 4.10 1.33
scd0 0.00 0.00 0.01 0.00 0.02 0.00 4.14 0.00 3.10 3.10 0.00 3.10 0.00
dm-0 0.00 0.00 2.38 0.96 124.08 55.63 107.60 0.19 58.09 16.74 159.94 3.94 1.32
dm-1 0.00 0.00 0.03 0.42 0.14 1.68 8.00 0.06 130.92 21.02 139.85 0.65 0.03
從上面我們可以看出有4個設備的IO讀寫信息,我還提供了一個ALL 的選項,該選項是計算所有IO設備讀寫之和。
關鍵代碼:
@Override
public void run() {
while(true) {
//prepare the collect
in = linuxcommand.getInfoReader();
String line = null;
try {//skip three lines
line = in.readLine();
line = in.readLine();
line = in.readLine();
} catch (IOException e2) {
e2.printStackTrace();
}
//collect the data
if(this.changed == true) {
this.timeseries1.clear();
this.timeseries2.clear();
value1 = value2 = 0.0D;
//change the show state
this.changed = false;
}
if(state.equals("all") == true) {
try {
value1 = value2 = 0.0D;
while((line = in.readLine()) != null) {
if(line.equals("") == true)
break;
String[] temp = line.split("[\\s]+");
value1 += Double.parseDouble(temp[5]);
value2 += Double.parseDouble(temp[6]);
}
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}else {
try {
while((line = in.readLine()) != null) {
if(line.equals("") == true)
break;
String[] temp = line.split("[\\s]+");
if(temp[0].equals(this.state)) {
value1 = Double.parseDouble(temp[5]);
value2 = Double.parseDouble(temp[6]);
break;
}
}
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
this.fillTimeSeries(value1, value2);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
linuxcommand.processDestroy();
}
}
代碼注釋少,一部分原因是在win 10寫的中文注釋在linux下會亂碼和我英文不好,一部分原因是我懶,主要是因爲懶。T_T。
6. Memory
這個類是實現了動態分析内存的使用率,遇到的linux指令是/proc/meminfo
,需要的數據衹是前五行。
% = (MemTotal - MemFree - Buffers - Cached) / MemTotal * 100
備注:rhl5下這條指令的結果沒有MemAvailable, 并且我門也不使用它。(這就是我程序的局限性,換了臺機子,估計就跑不正確了。)
[root@localhost class]# cat /proc/meminfo
MemTotal: 1870516 kB
MemFree: 112008 kB
MemAvailable: 401968 kB
Buffers: 72 kB
Cached: 384544 kB
SwapCached: 4120 kB
Active: 894496 kB
.........................
關鍵代碼:
@Override
public void run() {
while(true) {
in = linuxcommand.getInfoReader2();
String line = null;
try {
for(int i=0 ; i<5 ; ++i) {
line = in.readLine();
mem[i] = Long.parseLong(line.split("\\s+")[1]);
}
in.close();
} catch (IOException e2) {
e2.printStackTrace();
}
//Fulfill the dataset
for(int i=0 ; i<19 ; ++i) {
value[i] = value[i+1];
categoryDataset.addValue(value[i], "", "" + i);
}
value[19] = 100.0D * (mem[0] - mem[1] - mem[3] - mem[4]) / mem[0];
categoryDataset.addValue(value[19], "", "19");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
7. 編譯運行
把所有源代碼放在一個文件下,比如説src
編譯:
進入源代碼目錄下:
cd ../src
//將輸出的字節碼文件統一放到該目錄下的class文件夾下
javac -d class *.java
運行:
進入該目錄下的class文件夾下
cd class
//我的main函數在類MainFrame里
java MainFrame