Статьи

Netty: сервер другого типа (Socket)

Netty сегодня используется во всех видах приложений по всему Интернету для обработки тысяч (если не миллионов) чатов, многопользовательских игр, включая Minecraft , Twitter и многие другие приложения. Тем не менее, это не сделало это слишком далеко в голову корпоративных программистов, разрабатывающих бизнес-приложения.

Я считаю, что Netty может представить новую волну функциональности, которой другие решения просто не могут соответствовать из-за своей полностью двунаправленной передачи текста и двоичных данных, не относящихся к HTTP, а также благодаря поддержке гораздо большего числа одновременных клиентов, чем традиционный поток-сокет Серверы.

Вы можете знать о мастерстве Netty с WebSockets, но знаете ли вы, что он может функционировать очень хорошо, как традиционный веб-сервер? Благодаря очень продуманному дизайну Netty может обрабатывать практически любой трафик. Он также может обрабатывать несколько типов одновременно, например, WebSockets и HTTP через один и тот же порт одновременно. Комбинируя их вместе, программисты избавляются от таких неприятностей, как CORS (Cross Origin Resource Sharing), которые могут создать уродливую голову, когда браузер пытается отправлять запросы на серверы, с которых он не загружается.

Сила Нетти

Чтобы дать представление о его способности трансформировать корпоративные приложения, я собрал пример кода, показывающий один из традиционных примеров Интернета, который извлекает цены на акции.

Другие приложения должны будут делать запросы AJAX, опрашивать, иметь кнопки обновления и т. Д. Для обновления цен. WebSockets устраняет необходимость в этом. После создания постоянно открытого двунаправленного соединения и клиент, и сервер могут общаться друг с другом, когда это необходимо, без каких-либо согласований. Таким образом, клиент сообщает серверу, когда какой-либо пользователь изменяет критерии, и сервер обновляет клиент всякий раз, когда соответствующие данные изменяются на основе этих критериев.

  • Вы можете найти полностью функциональный код здесь .

Я настроил небольшой протокол на основе JSON для клиента, чтобы сервер знал, что решил пользователь. Чтобы добавить новый символ в список, за которым сервер следит за клиентом, достаточно простого вызова. Вот пример:

1
doSend('{"command":"add", "tickerSymbol":"GOOG"}');

Это добавляет символ в список. Следующее обновление с сервера включает текущую цену акций (из REST API Yahoo Finance) для нового символа в своих данных. Столь же легко удалить предмет:

1
doSend('{"command":"remove", "tickerSymbol":"GOOG"}');

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

Давайте посмотрим, как определяются обработчики для конвейера Netty. Это из класса StockTickerServer:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast("encoder", new HttpResponseEncoder());
p.addLast("decoder", new HttpRequestDecoder());
p.addLast("aggregator", new HttpObjectAggregator(65536));
p.addLast("handler", new StockTickerServerHandler());
}
});

Порядок здесь очень важен, так как каждый обработчик в конвейере имеет возможность обрабатывать (или не обрабатывать) данные и передавать их следующему обработчику. Обработчик биржевого тикера находится внизу, так как именно он отправляет данные обратно клиенту и поэтому находится в конце конвейера. Создавая новые экземпляры обработчиков, каждое новое соединение получает свои экземпляры каждого обработчика. Если обработчики не сохраняют состояние и поддерживают поток, то вместо этого можно использовать синглтоны для экономии памяти. Ни один из используемых мной обработчиков не является разделяемым, поэтому я не буду показывать пример этого.

Netty как веб-сервер

Несколько приемов используются для того, чтобы Netty обрабатывал HTTP и трафик WebSocket одновременно.

1. StockTickerServerHandler расширяет SimpleChannelInboundHandler <Объект>

Это говорит Netty, что мы хотим, чтобы весь трафик приходил на этот обработчик. В противном случае мы могли бы использовать SimpleChannelInboundHandler <FullHttpRequest>, если мы хотим обрабатывать только HTTP-трафик, или SimpleChannelInboundHandler <WebSocketFrame>, если мы хотим обрабатывать только трафик WebSocket.

2. Метод channelRead0 (чтение канала ноль)

1
2
3
4
5
6
7
8
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
this.handleHttpRequest(ctx, (FullHttpRequest)msg);
} else if (msg instanceof WebSocketFrame) {
this.handleWebSocketFrame(ctx, (WebSocketFrame)msg);
}
}

Это позволяет нам обрабатывать трафик HTTP и WebSocket в соответствии с каждым протоколом. handleHttpRequest обслуживает HTML, изображения, CSS, JavaScript и весь другой обычный веб-трафик, а handleWebSocketFrame выясняет, что делать с пользовательскими сообщениями, отправляемыми с клиента.

3. Типы пантомимы

Netty не имеет встроенной поддержки обработки MIME-типов, поскольку вызовы WebSocket по сути не нужны.

Я добавил слегка измененную версию Apache MIME-типов и загрузил ее статически. Я синхронизируюсь по загрузке, потому что Netty может создать довольно много обработчиков при запуске для пула, если хочет, и конструктор может выполняться многими обработчиками одновременно. Поскольку поле является статическим, карта может быть загружена много раз, прежде чем она станет ненулевой. Синхронизация на статической блокировке (НЕ текущий экземпляр класса) предотвращает это.

Другие детали

Метод handleWebSocketFrame заботится о различных «известных» типах фреймов, которые определяет протокол WebSocket . После получения полнотекстового фрейма я передаю его создателю созданного мной интерфейса, чтобы указать, как работать с бизнес-логикой.

Этот код живет в StockTickerMessageHandler. Он создает фоновый поток для извлечения биржевых котировок и отправки их клиенту, а также обрабатывает команды, отправляемые клиентом.

Там есть немного грязного кода для обработки сжатых Gzip данных, отправляемых Yahoo, и анализа JSON, возвращаемого службой, а также некоторый код, который использует классы java.util.concurrent, такие как Executor, AtomicBoolean, AtomicReference и CopyOnWriteArrayList, чтобы сохранить Фоновый поток и обработчик Netty не могут наступать друг на друга, так как они делятся информацией о канале и текущем списке символов.

Я также использую Gson, чтобы превратить входящий JSON в POJO, чтобы их было легче обрабатывать. Кроме этого, это просто бизнес конец этого примера.

Примечание об аутентификации

У меня не было времени добавить аутентификацию в этот пример. Если бы я это сделал, я бы использовал Shiro , сверхмощную инфраструктуру аутентификации / авторизации / шифрования, которая работает как с обычными приложениями, так и с веб-приложениями. Поддержка HTTPS также отсутствует, так как это публичное приложение для проверки цен на акции. Вот пример для добавления HTTPS (и WSS) здесь .

Одна вещь, которая очень трудна (если не невозможна) с JavaScript WebSockets — это отправка данных аутентификации вместе с запросом на обновление (то есть вызов нового WebSocket (uri)). По этой причине обычно сначала отправляют сообщение HTTPS POST, как это делается на обычном веб-сайте, и устанавливают токен cookie cookie. Таким образом, при отправке запроса на обновление, cookie автоматически отправляется вместе с ним. При использовании аутентификации не забывайте использовать HTTPS и WSS вместо HTTP и WS для защиты данных. Как только аутентификация установлена, она становится вопросом проверки для аутентифицированного пользователя, где это необходимо, отмечая, что некоторый трафик должен всегда проходить (HTML, изображения и т. Д.).

Вывод

Netty зарекомендовала себя как высокопроизводительный, изменяющий игру способ создания новых приложений. Современные корпоративные приложения могут быть гораздо более интерактивными, чем сейчас, используя возможности, предлагаемые WebSockets. Я надеюсь, что вам понравилось это маленькое приключение в Netty, и, пожалуйста, простите за ужасный браузерный клиент, у меня просто не было времени, чтобы сделать хорошее клиентское приложение Backbone.js для этого примера.

Благодаря!

Ссылка: Netty: сервер другого типа (Socket) от нашего партнера JCG Джона Бордмана в блоге