JVM Profiler IOProfiler

开篇

 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 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> cpuTime = ProcFileUtils.getProcStatCpuTime();
        
        Map map = new HashMap();

        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 selfMap = new HashMap();
        map.put("self", selfMap);
        
        Map ioMap = new HashMap();
        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  getProcIO() {
        return getProcFileAsMap(PROC_SELF_IO_FILE);
    }

    public static Map getProcFileAsMap(String filePath) {
        try {
            File file = new File(filePath);
            if (!file.exists() || file.isDirectory() || !file.canRead()) {
                return Collections.emptyMap();
            }

            Map result = new HashMap<>();
            List 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> getProcStatCpuTime() {
        List rows = getProcFileAsRowColumn(PROC_STAT_FILE);
        return getProcStatCpuTime(rows);
    }


    public static List getProcFileAsRowColumn(String filePath) {
        try {
            File file = new File(filePath);
            if (!file.exists() || file.isDirectory() || !file.canRead()) {
                return Collections.emptyList();
            }
            // 获取文件的所有行,每行的保存格式是List
            List result = new ArrayList<>();
            List 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> getProcStatCpuTime(Collection rows) {
        if (rows == null) {
            return Collections.emptyList();
        }
        
        final int minValuesInRow = 6;

        List> result = new ArrayList<>();
        
        for (String[] row : rows) {
            if (row.length >= minValuesInRow && row[0].toLowerCase().startsWith("cpu")) {
                Map 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": "[email protected]",
    "host": "local",
    "processUuid": "d32879c3-3ebb-4228-a649-2821c369a30d",
    "self": {
        "io": {
            "wchar": null,
            "write_bytes": null,
            "rchar": null,
            "read_bytes": null
        }
    },
    "epochMillis": 1536102327045,
    "tag": "mytag"
}

你可能感兴趣的:(JVM Profiler IOProfiler)