Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Алексей Рагозин, Артём Панасюк — Распределённое нагрузочное тестирование на Java

Алексей Рагозин, Артём Панасюк — Распределённое нагрузочное тестирование на Java

Основной принцип PTDD (performance test driven development) — начинать регулярное нагрузочное тестирование, как только появляется код, который можно тестировать. Зачастую это означает, что инфраструктура распределённого нагрузочного тестирования должна появляться раньше самого приложения и эволюционировать с его развитием. Это делает непрактичным использование традиционных деплоймент тулов (chef, ansible, и т.п.).

Предлагаемое решение задачи — полный стек автоматизации распределённого нагрузочного тестирования на Java.

В докладе:
• Почему инструментом выбрана Java и в чём преимущества "монокультурного" подхода.
• "9 fallacities of distributed computing" с точки зрения распределённых тестов.
• От императивной Явы, к массивно-параллельной императивной Яве.
• Фреймворк: архитектура, компоненты, использование.

Фреймворк находится в свободном доступе и распространяется под лицензией Apache 2.0

Moscow JUG

January 20, 2016
Tweet

More Decks by Moscow JUG

Other Decks in Programming

Transcript

  1. Распределённое нагрузочное
    тестирование на Java
    Алексей Рагозин
    Артём Панасюк
    jug.msk.ru 2016

    View Slide

  2. В докладе
    • Введение
    • Концепция PTDD
    Performance Test Driven Development
    • Распределённый нагрузочный тест – что это?
    • Стек инструментов
    • Пример теста

    View Slide

  3. Тестирование
    – инструмент познания мира

    View Slide

  4. Performance Test Driven Development
    Функциональное тестирование
    Нагрузочное тестирование
    Cost of bug
    Test volume
    Release
    Release
    Test volume
    Cost of bug

    View Slide

  5. Performance Test Driven Development
    • Пишем неоптимизированный код
    • Пишем нагрузочные тесты / бенчмарк
    • Исправляем проблемы производительности
    • Организуем изолированные тесты в профили
    нагрузки по мере добавления функционала
    • Непрерывное тестирование производительности

    View Slide

  6. Как тестировать до “начала”?
    Нет скрипов, инфраструктуры, приложения
    Надо тестировать базовые сценарии
    Тестировать на реальном масштабе
    Тестировать сценарии отказов
    Дизайн меняется на ходу, тесты вместе с ним
    Тесты должны гонять себя сами, у
    разработчиков хватает работы.

    View Slide

  7. Как тестировать до “начала”?
    На чём вы пишите юнит-тесты?
    • JUnit - Java
    Почему бы не писать нагрузочные также?
    • Мы знаем Java
    • На Java есть библиотеки для всего
    • Никаких переформанс инженеров
    тесты пишут разработчики

    View Slide

  8. Вопрос культуры
    “Классический” подход
    • bash + ssh + анализ логов + Excel / R
    • Мало пригоден для повторного использования
    • Короткий период полураспада тестов
    • Использование незнакомого инструментария
    “Монокультурный” подход
    • Платформа приложения = платформа автоматизации
    − Приходится изобретать велосипеды, но
    + Решается проблема культурного диссонанса

    View Slide

  9. Нагрузочный тест
    Развёртывание
     В распределённой среде
    Подготовка данных / разогрев
    Сценарий тестирования
     Например, дневной профиль нагрузки с отказом узла в кластера
    Сбор метрик
     Тестовые события
     Метрики ОС
     Данные профилирования
    Подготовка отчёта

    View Slide

  10. Стек инструментов
    Nanocloud - https://github.com/gridkit/nanocloud/
     Развертывание / управление распределённой средой
    GridBeans – “массивно параллельная” Java
     Оркестрация развертывая и подготовки
     Выполнение сценария
    Nimble
     Компоненты: cбор данных, мониторинг и прочее
    Отчёты
     Сырой CSV на выходе, решение не найдено

    View Slide

  11. Nanocloud

    View Slide

  12. Nanocloud
    Удалённое выполнения Java кода
    Просто как …
    @Test
    public void hello_remote_world() {
    Cloud cloud = CloudFactory.createSimpleSshCloud();
    cloud.node("myserver.acme.com").exec(new Callable(){
    @Override
    public Void call() throws Exception {
    String localhost = InetAddress.getLocalHost().toString();
    System.out.println("Hi! I'm running on " + localhost);
    return null;
    }
    });
    }

    View Slide

  13. All you need is …
    NanoCloud requirements
     SSHd
     Java (1.6 and above) present
     Works though NAT and firewalls
     Works on Amazon EC2
     Works everywhere where SSH works

    View Slide

  14. Master – slave communications
    Master process Slave host
    SSH
    (Single TCP)
    Slave
    Slave
    RMI
    (TCP)
    std err
    std out
    std in
    diag
    Slave
    controller
    Slave
    controller
    multiplexed slave streams
    Agent

    View Slide

  15. Death clock is ticking
    Master JVM kills slave processes, unless
     SSH session was interrupted
     someone kill -9 master JVM
     master JVM has crashed (e.g. under debuger)
    Death clock is ticking on slave though
     if master is not responding
     slave process will terminate itself

    View Slide

  16. Cloud scale JVM
    Same API – different topoligies
     in-process (debug), local, remote (distributed)
    Transparent remoting
    SSH to manage remote server
    Automatic classpath replication (with caching)
    Zero infrastructure
     Any OS for master host
     SSHd + JVM for slave hosts (Unix / Cygwin)
    200+ slave topology in routinely used

    View Slide

  17. Распределённый
    сценарий

    View Slide

  18. Распределённый сценарий
    • Простые шаги (реализованные на Java)
    • Выполняемые в распределённой системе
    согласно сценарию
    • Возможность синхронизации шагов
    • Обмен данными между шагами
    • Нет циклов и условного выполнения
    • Fail fast – ошибка любого шага
    завершает сценарий

    View Slide

  19. Mockito
    @Test
    public void test() {
    MyList mock = Mockito.mock(MyList.class,
    Mockito.RETURNS_DEEP_STUBS);
    Mockito.when(mock.get(0).getValue()).thenReturn("v1");
    Mockito.when(mock.get(1).getValue()).thenReturn("v2");
    Assert.assertEquals(null, mock.get(2).getValue());
    Assert.assertEquals("v2", mock.get(1).getValue());
    Assert.assertEquals("v1", mock.get(0).getValue());
    InOrder io = Mockito.inOrder(mock);
    io.verify(mock).get(2);
    io.verify(mock).get(1);
    io.verify(mock).get(0);
    io.verifyNoMoreInteractions();
    }

    View Slide

  20. Mockito way
    1. Декларация намерений
     Моки и вызовы методов
     “Виртуальное” время
     “Абстрактная” распределённость
    2. Физическое выполнение
     Привязка к топологии
     Зависимости между шагами
     Параллельное / распределённое выполнение

    View Slide

  21. Простой пример #1
    public void example() {
    MonadBuilder bld = MonadFactory.build();
    MyReader reader = bld.locator(Cloud.class).at("reader")
    .deploy(MyReader.class, ...);
    reader.configure("read config");
    bld.join("start");
    reader.execute();
    bld.join("done");
    bld.rewind();
    MyWriter writer = bld.locator(Cloud.class)
    .at("writer").deploy(MyWriter.class, ...);
    writer.configure("write config");
    bld.join("start");
    writer.execute();
    bld.join("done");
    }

    View Slide

  22. Простой пример #1
    deploy
    reader
    reader
    .configure()
    start
    reader
    .execute()
    done
    deploy
    writer
    writer
    .configure()
    writer
    .execute()

    View Slide

  23. Простой пример #1
    Драйвер
     Интерфейс декларирующий шаги
     Шаги – методы
     Шаг может вернуть “интерфейс”
     Шаги могут принимать параметры
     Сериализуемые объекты
     Результаты выполнения шагов
     Реализация драйвера размещается в
    распределённой среде используя локатор

    View Slide

  24. Простой пример #1
    public void example() {
    MonadBuilder bld = MonadFactory.build();
    MyReader reader = bld.locator(Cloud.class).at("reader")
    .deploy(MyReader.class, ...);
    reader.configure("read config");
    bld.join("start");
    reader.execute();
    bld.join("done");
    bld.rewind();
    MyWriter writer = bld.locator(Cloud.class).at("writer")
    .deploy(MyWriter.class, ...);
    writer.configure("write config");
    bld.join("start");
    writer.execute();
    bld.join("done");
    }
    Локатор
    Драйвер прокси
    Checkpoint
    Checkpoint
    Перемотка времени

    View Slide

  25. Простой пример #1
    deploy
    reader
    reader
    .configure()
    start
    reader
    .execute()
    done
    deploy
    writer
    writer
    .configure()
    writer
    .execute()
    deploy
    reader
    reader
    .configure()
    reader
    .execute()
    deploy
    writer
    writer
    .configure()
    writer
    .execute()

    View Slide

  26. Простой пример #2
    MyReader reader = bld.locator(Cloud.class).at("reader")
    .deploy(MyReader.class, ...);
    reader.configure("read config");
    bld.sync();
    Runnable readTask = reader.createTask();
    bld.join("start");
    ExecutionDriver exec = bld.bean(ExecutionDriver.class);
    Activity readJob = exec.execute(readTask);
    bld.join("stop");
    readJob.stop();
    bld.join(readJob);
    bld.join("done");
    bld.rewind("start");
    bld.wallclock().delay(30, TimeUnit.SECONDS);
    bld.join("stop");

    View Slide

  27. Простой пример #2
    deploy
    reader
    reader
    .configure()
    start
    wallclock
    .delay()
    done
    reader
    .createTask()
    exec
    .execute()
    stop
    readTask
    readJob
    .stop()
    readJob
    readJob
    .join()

    View Slide

  28. Простой пример #2
    MyReader reader = bld.locator(Cloud.class).at("reader")
    .deploy(MyReader.class, ...);
    reader.configure("read config");
    bld.sync();
    Runnable readTask = reader.createTask();
    bld.join("start");
    ExecutionDriver exec = bld.bean(ExecutionDriver.class);
    Activity readJob = exec.execute(readTask);
    bld.join("stop");
    readJob.stop();
    bld.join(readJob);
    bld.join("done");
    bld.rewind("start");
    bld.wallclock().delay(30, TimeUnit.SECONDS);
    bld.join("stop");
    Вспомогательный сервис
    Интерфейс фоновых задач
    Получение сервиса

    View Slide

  29. Простой пример #2
    Ошибка выполнение шага отлавливается
    и завершает сценарий.
    Когда создаётся фоновая задача, используется
    Activity.join() шага, который не завершится пока
    задача не будет остановлена.
    Wallclock – драйвер предоставляемый из коробки.
    ExecutionDriver – пример вспомогательного
    драйвера.

    View Slide

  30. Распределённый сценарий
    Всех аспекты теста –
    Развёртывание / Сбор данных / Мониторинг / и т.д.
    шаги / драйверы единого сценария

    View Slide

  31. Распределённый сценарий
    Fallacies of distributed computing
    https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing
    • The network is reliable – fail test fast.
    • Latency is zero – small number of round trip.
    • Bandwidth is infinite – low bandwidth control flow.
    • The network is secure – irrelevant for testing.
    • Topology doesn't change – fail test fast and rerun.
    • There is one administrator – zero deployment.
    • Transport cost is zero – there is no free lunch 
    • The network is homogeneous – location conscious scenario.

    View Slide

  32. Вспомогательные
    компоненты

    View Slide

  33. Метеринг
    Сбор метрик в распределённой среде
     Данные тестовых событий
     Данные мониторинга
    Метрики аккумулируются на локальном
    диске и передаются на управляющий узел
    по завершении сценария
    Все результаты экспортируются в CSV файл
    большой CSV файл

    View Slide

  34. Мониторинг
    Метрики OS - https://github.com/hyperic/sigar
     Network interface stats
     Process stats (поиск нужных процессов)
    JVM
     GC
     Threads
     Custom MBeans
    Build your own

    View Slide

  35. Профилирование
    BTrace 2 - https://github.com/jbachorik/btrace2
     Инструментирующий профайлер
     Точки перехвата на Java
     Развёртывание как часть сценария
     Интеграция с метерингом

    View Slide

  36. Ссылки
    NanoCloud
    • https://github.com/gridkit/nanocloud/
    • https://code.google.com/p/gridkit/wiki/NanoCloudTutorial
    • Maven Central: org.gridkit.lab:nanocloud:0.8.10
    • http://blog.ragozin.info/2013/01/remote-code-execution-in-java-made.html
    GridBeans
    • https://github.com/aragozin/gridbeans
    Nimble
    • https://github.com/gridkit/nimble
    Пример бенчмарка
    • https://github.com/gridkit/zk-benchmark-example

    View Slide

  37. Thank you
    Alexey Ragozin
    [email protected]
    http://blog.ragozin.info
    - my articles
    http://www.meetup.com/bigmoscow
    - community events in Moscow

    View Slide