Статьи

Копаем в розетки с помощью Java Flight Recorder

Давайте рассмотрим один удивительный инструмент для сбора информации о том, что происходит в вашей JVM — Java Flight Recorder.
Давайте рассмотрим один удивительный инструмент для сбора информации о том, что происходит в вашей JVM. Этот инструмент называется 
Java Flight Recorder,  и в сегодняшней статье я собираюсь использовать его, чтобы понять, как
Sockets ведет себя в вашем Java-приложении.

Многие люди используют API-интерфейсы HTTP для связи между своими службами (давайте напишем еще одну статью о том, является ли это хорошим решением или нет), и в большинстве случаев он работает нормально. Тем не менее, мы можем попасть в ситуации, когда производительность действительно имеет значение, и особенно для разработчиков библиотек, JFR может быть очень полезным инструментом, чтобы увидеть работу ввода-вывода в их коде.


Вам также может понравиться:
Использование Java Flight Recorder с OpenJDK 11

Эта статья не является введением в Flight Recorder, она более практична. Если вы хотите узнать больше об этой технологии, вы можете начать с JEP 328: Flight Recorder .

Введение в наше приложение Netty

В качестве примера я решил использовать отличный проект под названием Netty,  который упрощает реализацию JDK NIO и предоставляет множество приемов, которые мы можем использовать для оптимизации нашего приложения. Давайте углубимся в то, что мы собираемся использовать для наших экспериментов.

Не стесняйтесь проверить мой проект jfr-sockets  на GitHub и попробовать все со мной. Проект фактически содержит два приложения на основе TCP. Первый в простом пакете  — это просто дамп TCP Client / Server, и клиент отправляет каждую 1-ю строку my-message, которая занимает ровно 10 байтов. И второе — это клиент-серверное приложение на базе Netty, где сервер отправляет каждые 1 ту же строку, что и раньше, но с использованием WebSocket. Я сознательно выбрал эти типы приложений, и мы рассмотрим детали того, почему я это сделал.

Вы также можете заметить строковую константу с именем  CONFIG . Я использую трюк, в котором файл с правильной конфигурацией создается таким образом, вместо изменения обычного файла конфигурации JFR. Если вы хотите увидеть файлы конфигурации в JDK, пожалуйста, откройте эту папку ${JAVA_HOME}/lib/jfr Скорее всего, вы заметили два файла default.jfc и profile.jfc . Если вы откроете его, вы увидите XML-конфигурацию событий JFR. Эта конфигурация говорит, когда событие должно быть отправлено. По умолчанию  файл конфигурации имеет меньшие издержки (~ 1%) и полезен для непрерывного профилирования, тогда как  конфигурация профиля  очень агрессивна и генерирует больше событий с более низкими порогами (накладные расходы ~ 2%).

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

Обратите внимание, что я использую новую функцию, которая доступна с Java 14  JEP 349: Потоковая передача событий JFR , а это означает, что если вы хотите попробовать ее, вам нужно скачать ее с  https://jdk.java.net/14/ .

Запустите простое блокирующее приложение

Самый первый пример — это класс  pbouda.jfr.sockets.simple.Start . Если вы попробуете это, вы увидите вывод, похожий на приведенный ниже. Как мы упоминали ранее, приложение записывает UTF String my-message (10 байт), и если вы посмотрите внутрь методов, мы фактически попали в stacktrace поле, 10-байтовая строка кодируется в 12-байтовое сообщение.

Поскольку TCP основан на потоках, нам нужен хорошо известный разделитель между двумя сообщениями, или нам нужно как-то кодировать размер сообщения. Реализация JDK writeUTFи readUTF основана на втором варианте. JDK использует первые два байта как короткий  тип, который определяет количество байтов в самом сообщении.


Джава