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<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"
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值