пятница, 10 августа 2012 г.

Hyperic Sigar: мониторинг ресурсов в java

Иногда важно собирать информацию об использовании приложением системных ресурсов. Например, для сбора требуемой статистики. Или для обязательного совмещения с профилем нагрузки при нагрузочном тестировании. Или потому, что просто интересно :)
SIGAR (http://support.hyperic.com/display/SIGAR/Home) предоставляет переносимый интерфейс для сбора информации о системе и потреблении системных ресурсов:
* системная память, размер swap-файла, загруженность cpu, продолжительность работы;
* потребление памяти и cpu, а также состояние, окружение, параметры и используемые файлы по отдельным процессам;
* данные по файловой системе, очереди на запись/чтение, количество write/read байт, размеры дискового пространства;
* данные по сетевому интерфейсу, его конфигурации;
* таблица tcp/udp соединений, для них локальные/удалённые адреса, очереди на чтение/запись;
* таблица сетевых маршрутов и пр., и пр.

Собственно, эта информация так или иначе доступна на большинстве операционных систем, но везде - своим ОС-специфичным способом. Так что разработчик sigar проделал большую работу и предоставил единый api для доступа к подобной информации без привязки к конкретным платформам.
На странице проекта представлен перечень поддерживаемых платформ и архитектур, а также их версий (win, linux, mac, freebsd и т. д.).
Наиболее удобно работать с библиотекой из java; также в той или иной степени есть поддержка .net, perl, php, python, ruby.
Скачать можно здесь: http://sourceforge.net/projects/sigar/files/. На текущий момент последняя версия - 1.6.4, датирована 28 апреля 2010 года.
Имеется хорошая документация, удобная для чтения и понимания, которую можно найти по этому линку: http://www.hyperic.com/support/docs/sigar/. Также доступны jira и форум для обсуждения вопросов.
Основной класс - org.hyperic.sigar.Sigar, который предоставляет доступ к объектам, содержащим системную информацию. Т. о., он является прослойкой, позволяющей работать на одном api с разными платформенно-зависимыми объектами и данными. 


КАК ЭТО ВЫГЛЯДИТ В КОДЕ?
    private void run () throws SigarException {
        Sigar sigar = new Sigar();

        // enabling logging in the native Sigar code (log4j is required)
        sigar.enableLogging(true);

        Mem mem = sigar.getMem();
        System.out.println(mem.toString());

        Swap swap = sigar.getSwap();
        System.out.println(swap.toString());
        System.out.println("Total swap in readable format: " + Sigar.formatSize(swap.getTotal()));

        Cpu cpu = sigar.getCpu();
        System.out.println("CPU: " + cpu.toString());

        CpuPerc cpuPerc = sigar.getCpuPerc();
        System.out.println(cpuPerc.toString());

        CpuInfo[] cpuInfo = sigar.getCpuInfoList();
        for (CpuInfo temp : cpuInfo) {
            System.out.println(temp.toString());
        }

        ResourceLimit rLimit = sigar.getResourceLimit();
        // a range of values includes core, cpu, mem, opened files etc and depends on platform
        System.out.println("ResourceLimit: " + rLimit.toString());

        System.out.println("System uptime in seconds: " + sigar.getUptime().toString());

        long pid = sigar.getPid();
        // also it's possible to get a list of pids

        ProcState pState = sigar.getProcState(pid);
        System.out.println(pState.toString());
        // also you can get proc mem, state, time, cpu, credentials, descriptors,
        // current working directory, arguments, environment etc by PID

        int port = 12080;
        long pidByPort = sigar.getProcPort(NetFlags.CONN_TCP, port);
        System.out.println("Name of the process which uses port " + port + ": "
                        + sigar.getProcState(pidByPort).getName());

        FileSystem[] fileSystems = sigar.getFileSystemList();
        FileSystemUsage fsu = sigar.getFileSystemUsage(fileSystems[1].getDevName());
        System.out.println("File system usage: " + fsu.toString());
        // you can find disk reads/writes, total/avail/free size, disk queue etc
    }
ЧТО ПОЛУЧИТСЯ В РЕЗУЛЬТАТЕ?
Mem: 2086316K av, 1540164K used, 546152K free
Swap: 6119676K av, 1513396K used, 4606280K free
Total swap in readable format: 5.8G
CPU: {User=10110733, SoftIrq=0, Idle=694970327, Stolen=0, Wait=0, Total=1067346854, Irq=124233, Nice=0, Sys=6483452}
CPU states: 1.5% user, 1.5% system, 0.0% nice, 0.0% wait, 96.8% idle
{TotalSockets=2, Mhz=2533, Model=Core(TM)2 Duo CPU     E7200  @ 2.53GHz, TotalCores=2, Vendor=Intel, CoresPerSocket=1}
{TotalSockets=2, Mhz=2533, Model=Core(TM)2 Duo CPU     E7200  @ 2.53GHz, TotalCores=2, Vendor=Intel, CoresPerSocket=1}
ResourceLimit: {StackCur=325480, StackMax=327680, VirtualMemoryCur=2147483648, VirtualMemoryMax=2147483648}
System uptime in seconds: {Uptime=355742.0}
{Name=java, Threads=12, State=R, Ppid=14540, Priority=8}
Name of the process which uses port 12080: AvastSvc
File system usage: {DiskReads=64767, Used=184162392, Avail=123040536, DiskQueue=0.0, DiskReadBytes=658028032, DiskServiceTime=-1.0, DiskWriteBytes=1655718912, Free=123040536, Total=307202928, UsePercent=0.6, DiskWrites=146130}
ПРИМЕР МОНИТОРА (конечно, достаточно условный)
    private void monitor() throws IOException, SigarException, InterruptedException {
        Sigar sigar = new Sigar();

        // in this example, use PrintWriter
        File log = new File(System.currentTimeMillis()+".csv");
        PrintWriter out;
        out = new PrintWriter(
                new BufferedWriter(
                        new FileWriter(log)));

        out.println("Time;CPU load (%);Free memory (%)");

        for (int i = 0; i < 10; i++) {

            double cpuLoad = new BigDecimal(100 - sigar.getCpuPerc().getIdle() * 100)
                    .setScale(1, BigDecimal.ROUND_HALF_UP)
                    .doubleValue();

            double memFree = new BigDecimal(sigar.getMem().getFreePercent())
                    .setScale(1, BigDecimal.ROUND_HALF_UP)
                    .doubleValue();

            String time = new SimpleDateFormat("hh:mm:ss").format(new Date());

            out.println(new StringBuilder()
                    .append(time).append(";").append(cpuLoad).append(";").append(memFree)
                    .toString().replaceAll("\\.", ","));

            out.flush();

            Thread.sleep(1000);
        }
    }

Этот код будет каждую секунду фиксировать загруженность cpu и размер свободной оперативной памяти и сохранять данные построчно в csv-файл. А уж как анализировать полученные данные - строить графики в excel или прикручивать к какому-то другому анализатору - зависит от поставленной задачи.
Как видно из примеров, sigar api прост и интуитивно понятен. Более того, в исходниках проекта есть много примеров, способных помочь разобраться.

А РАБОТАТЬ В КОНСОЛИ МОЖНО?
 E:\SigarProject\lib\sigar>java -jar Sigar.jar

sigar> help
Available commands:
        alias          - Create alias command
        cpuinfo        - Display cpu information
        df             - Report filesystem disk space usage
        du             - Display usage for a directory recursively
        free           - Display information about free and used memory
        ...

sigar> cpuinfo
Vendor.........Intel
Model..........Core(TM)2 Duo CPU     E7200  @ 2.53GHz
Mhz............2533
Total CPUs.....2

sigar> quit
Goodbye.
PTQL

PTQL - это не ругательное слово, а сокращение от Process Table Query Language - механизма, с помощью которого можно определить процесс не только по его process id, но и по многим другим параметрам:
E:\SigarProject\lib\sigar>java -jar Sigar.jar

sigar> ps "Exe.Name.ct=java"
2624    user                205M     21M    -       R       0:2     javaw:com.zerog.lax.LAX
5556    user        12:24   431M     16M    -       R       0:0     java:org.hyperic.sigar.cmd.Runner

sigar> quit
Goodbye.
Команда ps запрашивает данные процесса, а "Exe.Name.ct=java" определяет условие - полное имя процесса должно содержать строку java. Подробнее http://support.hyperic.com/display/SIGAR/PTQL

ИТОГО

Итого, sigar - библиотека, полезная для мониторинга системных ресурсов. Она поддерживает большое число платформ, скрывает их особенности и предоставляет простой и удобный api для работы.

2 комментария:

  1. Спасибо за статью. Использовал как-то эту библиотеку в прошлом проекте, однако сейчас возникла необходимость получить более детальный обзор её возможностей.

    ОтветитьУдалить
    Ответы
    1. Рад, что информация оказалась полезной.
      Саму библиотеку использовал на практике при проведении нагрузочных тестов на машинах под управлением разными ОС, чтобы везде одним и тем же инструментом снимать потребление процессами памяти и ресурсов ЦП.
      С другими возможностями просто ознакомился, но применения им пока не нашлось.

      Удалить