пятница, 26 февраля 2016 г.

Rest сервис на Netty

netty framework logo
Недавно познакомился с фреймворком Netty. В традиционной модели на каждое соединение создается один поток, который его обрабатывает. Это приводит к росту нагрузки на CPU и объема потребляемой памяти. Netty использует другой подход, позволяющий обрабатывать одновременно количество соединений большее, чем количество запущенных потоков. Реализован он с использованием библиотеки java.nio. В данной статье, я опишу как Netty работает и расскажу как реализовать с его помощью простой REST сервис.
Для понимания принципа работы Netty с начало нужно обратиться к паттерну pipeline (Подробная статья с примером на .net). Если коротко, то данные проходят несколько этапов обработки. На каждом этапе они обрабатываются определенным обработчиком - handler'ом. Обработчик поочередно производит действия над данными, доставая их из своей входящей очереди. Как только handler закончил выполнение, полученный результат он отправляет следующему, у которого тоже есть очередь. Получается, что то вроде конвейера.

pipline

В Netty запросы обрабатываются похожим образом.
Теперь напишем REST сервис. У которого всего один метод sum - вычисляющий сумму двух чисел, переданных в качестве параметров "GET /sum?first=<первое число>&second=<второе число>". Ответ будет приходить формата plain text в следующем виде : "результат:<сумма двух чисел>".
Для начала добавим соответствующую зависимость. В этом примере используется версия 4.0.5.Final.


Теперь напишем handler'ы для обработки данных.
FilterHandler производит фильтрацию запросов, проверку параметров и отсеивание неверных. Его реализация:


После получения запроса проверим тот ли тип метода, к тому ли ресурсу происходит обращение и с теми ли параметрами. Полученные значения параметров записываем в заголовок запроса. А сам запрос добавляем в очередь, для обработки следующим handler'ом.
Следом реализуем WorkerHandler. Он вычисляет сумму и отправлять результат.


Теперь осталось самое малое, создать сервер, указать параметры и добавить в его pipeline наши обработчики.

В Netty существуют предустановленные handler'ы. В pipeline первые три: HttpResponseEncoder, HttpRequestDecoder, HttpObjectAggregator нужны лишь для того, чтобы сразу работать с объектом DefaultFullHttpRequest, иначе пришлось бы в ручную из набора байтов собирать запрос. CorsHandler служит для управления доступом к ресурсу (Подробнее, можно почитать здесь). Следом идут, созданные нами handler'ы.

Замечания:
  • Стоит обратить на некоторые нюансы. После записи ответа, обязательно нужно вызвать метод flush() и закрыть созданный при этом ChannelFuture. Если этого не сделать сервер будет держать соединение, пока оно не отвалится по timeout.
  • Важная деталь, в Netty используется механизм подсчета ссылок. Программист должен сам увеличивать количество ссылок вызовом метода retain() если объект будет использоваться где-то еще, или release() если действие над объектом закончены. Неправильное использование может привести к возникновению исключения IllegalReferenceCountException в лучшем случае, или же к утечкам памяти в худшем. (статья на официальном сайте)
  • Еще одна полезная функция в netty - ограничение скорости входящего и исходящего трафика. Можно избежать ситуаций saturated outbound, просто добавив в конце pipeline обработчик типа ChannelTrafficShapingHandler.