开篇
IOProfiler因为采集方法的限制,目前支持linux系统指标采集,但是不支持mac,windows等操作系统。
IOProfiler通过读取linux系统的/proc/self/io的当前线程的IO指标数据,该文件的内容如下图所示,通过解析成kv键值对完成采集。
[root@fuck logs]# cat /proc/3172/io
rchar: 598796271
wchar: 160798214
syscr: 3124807
syscw: 5936285
read_bytes: 307200
write_bytes: 134148096
cancelled_write_bytes: 184320
IOProfiler主要通过读取linux系统的/proc/stat来采集CPU的占用情况,该文件的内容如下图所示,通过解析成kv键值对完成采集。
[root@fuck logs]# cat /proc/stat
cpu 31965686 7863 22816368 10760846905 384719 21810 2723823 367110 0
cpu0 17337496 2161 12060256 2664404859 353464 21810 2715822 192461 0
cpu1 4445423 1896 3619627 2699436652 8995 0 3255 58954 0
cpu2 4421192 2028 3525381 2699115834 12552 0 2743 58589 0
cpu3 5761574 1776 3611102 2697889558 9706 0 2002 57104 0
源码分析
IO指标
- ProcFileUtils.getProcIO()完成IO指标采集。
- ProcFileUtils.getProcStatCpuTime()完成CPU指标采集。
- 将采集结果组装成Map后进行上报
public synchronized void profile() {
Map<String, String> procMap = ProcFileUtils.getProcIO();
Long rchar = ProcFileUtils.getBytesValue(procMap, "rchar");
Long wchar = ProcFileUtils.getBytesValue(procMap, "wchar");
Long read_bytes = ProcFileUtils.getBytesValue(procMap, "read_bytes");
Long write_bytes = ProcFileUtils.getBytesValue(procMap, "write_bytes");
List<Map<String, Object>> cpuTime = ProcFileUtils.getProcStatCpuTime();
Map<String, Object> map = new HashMap<String, Object>();
map.put("epochMillis", System.currentTimeMillis());
map.put("name", getProcessName());
map.put("host", getHostName());
map.put("processUuid", getProcessUuid());
map.put("appId", getAppId());
if (getTag() != null) {
map.put("tag", getTag());
}
if (getCluster() != null) {
map.put("cluster", getCluster());
}
if (getRole() != null) {
map.put("role", getRole());
}
Map<String, Object> selfMap = new HashMap<String, Object>();
map.put("self", selfMap);
Map<String, Object> ioMap = new HashMap<String, Object>();
selfMap.put("io", ioMap);
ioMap.put("rchar", rchar);
ioMap.put("wchar", wchar);
ioMap.put("read_bytes", read_bytes);
ioMap.put("write_bytes", write_bytes);
map.put("stat", cpuTime);
if (reporter != null) {
reporter.report(PROFILER_NAME, map);
}
}
IO指标采集
- 通过解析/proc/self/io文件来完成数据采集。
- 通过简单的kv解析来完成数据的采集。
private static final String PROC_SELF_IO_FILE = "/proc/self/io";
public static Map<String, String> getProcIO() {
return getProcFileAsMap(PROC_SELF_IO_FILE);
}
public static Map<String, String> getProcFileAsMap(String filePath) {
try {
File file = new File(filePath);
if (!file.exists() || file.isDirectory() || !file.canRead()) {
return Collections.emptyMap();
}
Map<String, String> result = new HashMap<>();
List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
for (String line : lines) {
int index = line.indexOf(VALUE_SEPARATOR);
if (index <= 0 || index >= line.length() - 1) {
continue;
}
String key = line.substring(0, index).trim();
String value = line.substring(index + 1).trim();
result.put(key, value);
}
return result;
} catch (Throwable ex) {
logger.warn("Failed to read file " + filePath, ex);
return Collections.emptyMap();
}
}
CPU指标
- 通过读取文件/proc/stat来完成数据采集。
- 通过解析带cpu开头的数据来完成数据采集。
private static final String PROC_STAT_FILE = "/proc/stat";
public static List<Map<String, Object>> getProcStatCpuTime() {
List<String[]> rows = getProcFileAsRowColumn(PROC_STAT_FILE);
return getProcStatCpuTime(rows);
}
public static List<String[]> getProcFileAsRowColumn(String filePath) {
try {
File file = new File(filePath);
if (!file.exists() || file.isDirectory() || !file.canRead()) {
return Collections.emptyList();
}
// 获取文件的所有行,每行的保存格式是List<String[]>
List<String[]> result = new ArrayList<>();
List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
// 遍历所有行,每一行以空格进行切割
for (String line : lines) {
result.add(line.split("\\s+"));
}
return result;
} catch (Throwable ex) {
logger.warn("Failed to read file " + filePath, ex);
return Collections.emptyList();
}
}
public static List<Map<String, Object>> getProcStatCpuTime(Collection<String[]> rows) {
if (rows == null) {
return Collections.emptyList();
}
final int minValuesInRow = 6;
List<Map<String, Object>> result = new ArrayList<>();
for (String[] row : rows) {
if (row.length >= minValuesInRow && row[0].toLowerCase().startsWith("cpu")) {
Map<String, Object> map = new HashMap<>();
try {
map.put("cpu", row[0]);
map.put("user", Long.parseLong(row[1].trim()));
map.put("nice", Long.parseLong(row[2].trim()));
map.put("system", Long.parseLong(row[3].trim()));
map.put("idle", Long.parseLong(row[4].trim()));
map.put("iowait", Long.parseLong(row[5].trim()));
result.add(map);
} catch (Throwable ex) {
continue;
}
}
}
return result;
}
采集结果
{
"stat": [],
"appId": null,
"name": "2343@xiaozhideMacBook-Pro.local",
"host": "local",
"processUuid": "d32879c3-3ebb-4228-a649-2821c369a30d",
"self": {
"io": {
"wchar": null,
"write_bytes": null,
"rchar": null,
"read_bytes": null
}
},
"epochMillis": 1536102327045,
"tag": "mytag"
}