Как измерить latency с помощью tcpdump

Для элементарных замеров latency в торговых приложениях на стадии разработки и QA часто используется перехватчик пакетов tcpdump, а пакеты анализируются с помощью приложения Wireshark.

tcpdump не применяют на PROD, потому что он сам оказывает влияение на производительность системы. Тонкие замеры latency на PROD производятся другими средствами, о чем я расскажу в свое время.

В данной статье я хочу продемонстрировать технику замера latency на примере двух моих приложений SimpleFIXClient и SimpleFIXExecutor. Надеюсь, пошаговая инструкция будет достаточно понятной и не слишком запутает вас.

Описание тестовой конфигурации

Напомню, приложение SimpleFIXExecutor эмулирует торговый движок биржи. Вы подключаетесь к нему по протоколу FIX, отправляете ему NOS, а SimpleFIXExecutor сначала отправляет ACK, а потом FILL, словно ордер исполнился на реальной бирже. SimpleFIXExecutor не делает никакого реального сведения ордеров. Вы можете посылать ему любой мусор, и он счастлив будет ответить вам ACK (да, сэр!) и FILL (ваш приказ исполнен, сэр!). SimpleFIXExecutor исполняет свое главное назначение: если у вас есть FIX-приложение, SimpleFIXExecutor станет для него задушевным собеседником, всегда готовым ответить!

В данном примере мы попытаемся получить ответ на вопрос: «Как быстро после получения NOS от FIX-клиента наш SimpleFIXExecutor отправляет ACK, а затем отправляет FILL?» Разница в timestamp между пакетом содержащим ACK и пкатом с NOS даст нам «внутреннюю» latency — время, которое уходит у SimpleFIXExecutor на обработку запроса. А разница между ACK и FILL — время на «заполнение» ордера.

tcpdump является утилитой для Linux, поэтому SimpleFIXExecutor будет запущен в виртуальной Linux-машине в VirtualBox, созданной с помощью Vagrant. А SimpleFIXClient будет запущен на той же машине Windows 10. Само собой разумеется, что все эти слои виртуализации дают свой вклад в задержки, но мы не ожидаем получить здесь никаких феноменальных показателей latency, цель статьи — продемонстрировать саму технику ее замера.

Создание виртуальной машины Linux

Для вашего удобства я создал конфигурацию для Vagrant и поместил ее в отдельном репозитарии. Проект содержит всего два файла:

/vagrant
│   README.md                                                                                                                                                                                                     │   Vagrantfile

Для запуска вирутальной машины потребуется добавить файлы:

  • Java: jdk-19_linux-x64_bin.rpm
  • Maven: apache-maven-3.8.7-bin.tar.gz
  • Gradle: gradle-7.6-bin.zip
  • Spring Boot: spring-boot-cli-2.7.7-bin.tar.gz
/vagrant
│   apache-maven-3.8.7-bin.tar.gz                                                                                                                                                                                 │   gradle-7.6-bin.zip                                                                                                                                                                                            │   jdk-19_linux-x64_bin.rpm                                                                                                                                                                                      │   README.md                                                                                                                                                                                                     │   spring-boot-cli-2.7.7-bin.tar.gz                                                                                                                                                                              │   Vagrantfile

Gradle собственно говоря не нужен, но я его ставлю обычно для всех проектов.

Vagrant создаст виртуальную машину, добавит нужные пакеты, установит Java, Maven, Spring Boot и откроет порт 9878, через который наш FIX-клиент будет общаться с FIX-сервером, работающим внутри виртуальной машины.

Vagrant по умолчанию монтирует папку проекта в папку /vagrant. Так что все файлы, помещенные в проект в Windows, будут видны в виртуальной машине в папке /vagrant.

В папке /vagrant создайте под-папку «project». Здесь мы будем хранить все файлы нашего эксперимента. В пакет «project» создайте подпапку «target», сюда мы будем складывать наши jar-файлы FIX-клиента и FIX-сервера. Дерево папок внутри виртуальной машины Linux выглядит так:

/vagrant
│   apache-maven-3.8.7-bin.tar.gz                                                                                                                                                                                 │   gradle-7.6-bin.zip                                                                                                                                                                                            │   jdk-19_linux-x64_bin.rpm                                                                                                                                                                                      │   README.md                                                                                                                                                                                                     │   spring-boot-cli-2.7.7-bin.tar.gz                                                                                                                                                                              │   Vagrantfile                                                                                                                                                                                                   └───project
    |
    └───target

Надеюсь, пока все понятно?

Собираем и запускаем SimpleFIXExecutor

SimpleFIXExecutor можно взять из моего репозитария. Проект SimpleFIXExecutor собирается с помощью «mvn clean install». В результате сборки в папке target проекта появляется jar-файл simplefixexecutor-0.0.1-SNAPSHOT.jar. Его следует скопировать в папку /vagrant/project/target/. Из проекта также скопируйте скрипт startServer.sh в папку /vagrant/project/.

Теперь дерево выглядит так:

/vagrant
│   apache-maven-3.8.7-bin.tar.gz                                                                                                                                                                                 │   gradle-7.6-bin.zip                                                                                                                                                                                            │   jdk-19_linux-x64_bin.rpm                                                                                                                                                                                      │   README.md                                                                                                                                                                                                     │   spring-boot-cli-2.7.7-bin.tar.gz                                                                                                                                                                              │   Vagrantfile                                                                                                                                                                                                   
|
└───project
    |   startServer.sh
    |
    └───target
           simplefixexecutor-0.0.1-SNAPSHOT.jar

В виртуальной машине из командной строки запустите скрипт ./startServer.sh. На экране пойдет вывод логов приложения. В конце концов SimpleFIXExecutor запустит несколько потоков в ожидании подключения клиентов по протоколу FIX. Клиентов, «разговаривающих» по протоколу FIX 4.2 SimpleFIXExecutor будет ожидать на порту 9878.

Собираем и запускаем SimpleFIXClient

SimpleFIXClient можно взять из моего репозитария. Проект SimpleFIXClient собирается с помощью «mvn clean install». В результате сборки в папке target проекта появляется jar-файл simplefixclient-2.0.jar. Его следует скопировать в папку /vagrant/project/target/. Из проекта также скопируйте скрипт startClient.bat в папку /vagrant/project/.

Для SimpleFIXClient нам понадобится еще несколько файлов:

  • definitions.dsl и simplefixclient.cfg положите в папку project
  • скопируйте целиком папки config и scenarios в папку project

Теперь дерево выглядит так:

/vagrant
│   apache-maven-3.8.7-bin.tar.gz                                                                                                                                                                                 │   gradle-7.6-bin.zip                                                                                                                                                                                            │   jdk-19_linux-x64_bin.rpm                                                                                                                                                                                      │   README.md                                                                                                                                                                                                     │   spring-boot-cli-2.7.7-bin.tar.gz                                                                                                                                                                              │   Vagrantfile                                                                                                                                                                                                   │                                                                                                                                                                                                                 └───project
     │   definitions.dsl
     │   simplefixclient.cfg
     │   startClient.bat
     │   startServer.sh
     │
     ├─── config
     │       application.properties
     │
     ├───scenarios
     │       Scenario1.groovy
     │
     └───target
             simplefixclient-2.0.jar
             simplefixexecutor-0.0.1-SNAPSHOT.jar

В Windows из командной строки запустите скрипт startClient.bat. Если все пройдет хорошо, SimpleFIXClient запустится, подключится к порту 9878, передаст два ордера по протоколу FIX с интервалом 2 секунды, получит ACK, а затем FILL по каждому из них, и завершит исполнение. Так SimpleFIXClient исполнит «сценарий», который записан в файле Scenario1.groovy в папке scenarios.

Если у вас что-то не запустилось или не получилось, пишите в комментариях, разберемся.

Запускаем SimpleFIXClient в длинном цикле

2 ордера это слишком мало, чтобы что-то успеть замерить. Давайте отправим SimpleFIXClient в длинный цикл отправки скажем 10.000 ордеров, чтобы он в фоне тихонечко работал, а мы снимали нужные нам мерки по мере надобности.

Откройте файл Scenario1.groovy. Найдите в файле сценария строку цикла:

for(x in 1..2) {

И поставьте 10000 вместо 2.

for(x in 1..10000) {

Теперь на отправку 10.000 ордеров с интервалом 2 секунды у SimpleFIXClient уйдет 20.000 секунд. А это сколько в минутах? 333,333 минуты. А сколько это в часах? 5,5 часов. Этого нам вполне хватит наиграться. Запускайте снова startClient.bat и пусть он тихонько работает в фоне. А мы приступим к самому вкусному.

Запускаем tcpdump и пишем пакеты в файл

Замечательная статья на Хабре Давайте изучим tcpdump с Джулией Эванс поможет вам освоить самые азы работы с tcpdump.

В отдельном окне терминала нашей виртуальной машины запустим следующую команду:

sudo tcpdump -n -i any port 9878 -w /vagrant/packets.pcap

В папке /vagrant появится файл packets.pcap. По мере наполнения буфера памяти tcpdump будет сбрасывать данные в файл. После завершения работы с помощью Ctrl-C все данные будут записаны в файл, которые мы будем анализировать с помощью Wireshark.

Смотрим дамп пакетов в Wireshark и вычисляем latency

Wireshark — бесплатная утилита для анализа сетевых пакетов. ее можно загрузить с сайта. Запускаем Wireshark и открываем файл packets.pcap с его помощью.

Wireshark показывает нам: Ethernet фрейм, в нем — IP-пакет, в нем TCP-пакет, а в нем — payload — «полезную нагрузку». Так как Wireshark по умолчанию не знает, что там в TCP мы передаем, он показывает наше сообщение в пакете как набор байтов. Давайте включим FIX-протокол в Wireshark с помощью пункта Analyze -> Enabled Protocols …, найдем там FIX и включим его:

На экране пока ничего не изменилось. Надо сказать теперь программе Wireshark, что пакеты, приходящие на порт 9878 и уходящие через этот порт, надо декодировать как FIX-сообщения. Выбираем любой пакет, кликаем правой кнопкой мышки на нем, выбираем пункт Decode As … и в колонке Current выбираем FIX.

Вот теперь на экран приятно смотреть:

Например, пакет 236. Видно, что это было FIX-сообщение NewOrderSingle. В ответ ему был отправлен ExecutionReport (ACK), а еще позднее — еще один ExecutionReport (FILL). Через 2 секунды к нам опять поступил NewOrderSingle. И эта комбинация повторяется каждые 2 секунды (90.ххх, 92.ххх, 94.ххх, 96.ххх…), что и ожидалось.

Теперь не составит труда посчитать latency. Пакет с NOS поступил в 94,168.601 (это столько секунд прошло с начала сбора пакетов), а ответный ACK прошел через tcpdump в 94,170.271. Получаем, 94,170.217 — 94,168.601 = 0,001.616, то есть на ответ понадобилось 1 миллисекунда и 616 микросекунд. Между ACK и FILL прошло: 94,170.743 — 94,170.217 = 0,000.526, т.е. 526 микросекунд.

Задача решена, ответ получен.

Что мы видим и почему?

Как видим показатели latency довольно удручающие. Почему они такие:

  • QuickFIX/J — не самая быстрая реализация FIX-протокола
  • мой домашний PC — не самая быстрая машина (процессор, память, сетевой интерфейс, куча процессо в фоне)
  • Java-приложение внутри виртуальной машины работает тоже медленнее из-за накладок на виртуализацию
  • Java-приложение написано с помощью Spring Boot, т.е. без всяких оптимизаций кода для достижения низкого latency
  • Java-приложение не достаточно «разогрето»
  • tcpdump сам добавляет нагрузку на виртуальную машину

Выводы

  • tcpdump требует прав администратора на установку и работу. На QA скорей всего вам его установит сетевой администратор.
  • tcpdump не рекомендуется ставить на PROD, так как он вносит дополнительную нагрузку на ядро системы и ее сетевые интерфейсы.
  • при анализе трафика, если tcpdump работает круглосуточно, его работу следует всегда ограничивать по порту и по IP-адресу, с которым вы обмениваетесь пакетами. В противном случае файл с дампами пакетов займет очень скоро все дисковое пространство и ваша система рухнет.
  • файл с записью пакетов лучше всего ротировать и хранить историю, скажем, за пять предыдущих дней, а все остальные удалять по мере «старения». Это следует делать также во избежание исчерпания места на диске.

Ссылки

Реклама

3 комментария на “Как измерить latency с помощью tcpdump

  1. Alexander Peshkov

    Круто, спасибо! Плачевно, что Wireshark поставить в проекте e’ impossibile 🙂 на раб машину: остается только домашние условия
    Ждем новых сторий из мира алго/dma/

    Нравится 1 человек

    1. alexk Автор записи

      согласен. Wireshark не приветствуется админами. У нас в банке он ставился на Windows desktop только с разрешения начальства и объяснением для чего это нужно, и с клятвой, что не будет использоваться для вредных действий.

      Нравится 1 человек

      1. Alexander Peshkov

        На самом деле на помощь может прийти python (например scapy, pyshark), правда придется «писать свой велосипед» по парсингу pcap file.

        Нравится 1 человек

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s