Netty + ProtoBuffer: несколько коммуникационных сообщений для одного соединения

Читая руководство по Netty, я нашел простое описание о том, как интегрировать Netty и буферы протокола Google. Я начал исследовать его пример (поскольку в документации больше нет информации) и написал простое приложение, подобное примеру приложения локального времени. Но в этом примере используется статическая инициализация в классе PipeFactory, например:

import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.handler.codec.protobuf.ProtobufDecoder;
import org.jboss.netty.handler.codec.protobuf.ProtobufEncoder;
import org.jboss.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import org.jboss.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

import static org.jboss.netty.channel.Channels.pipeline;

/**
 * @author sergiizagriichuk
 */
class ProtoCommunicationClientPipeFactory implements ChannelPipelineFactory {

    public ChannelPipeline getPipeline() throws Exception {
        ChannelPipeline p = pipeline();
        p.addLast("frameDecoder", new ProtobufVarint32FrameDecoder());
        p.addLast("protobufDecoder", new ProtobufDecoder(Communication.DataMessage.getDefaultInstance()));

        p.addLast("frameEncoder", new ProtobufVarint32LengthFieldPrepender());
        p.addLast("protobufEncoder", new ProtobufEncoder());

        p.addLast("handler", new ProtoCommunicationClientHandler());
        return p;
    }

}

(Пожалуйста, взгляните на строку p.addLast("protobufDecoder", new ProtobufDecoder(Communication.DataMessage.getDefaultInstance()));) и только одна фабрика может быть создана (как я понимаю) для класса ClientBootstrap, я имею в виду метод bootstrap.setPipelineFactory(). Итак, в этой ситуации я могу использовать ОДНО сообщение для отправки на сервер и ОДНО сообщение для получения с сервера, и это плохо для меня, и я думаю, не только для меня: ( Как я могу использовать разные сообщения только для одного соединения? Возможно, я могу создать несколько protobufDecoder, подобных этому

p.addLast("protobufDecoder", new ProtobufDecoder(Communication.DataMessage.getDefaultInstance()));
p.addLast("protobufDecoder", new ProtobufDecoder(Communication.TestMessage.getDefaultInstance()));
p.addLast("protobufDecoder", new ProtobufDecoder(Communication.SrcMessage.getDefaultInstance()));

или другие техники? Большое спасибо.


person Sergii Zagriichuk    schedule 18.10.2011    source источник
comment
Вы можете добавить много декодеров/кодировщиков в конвейер, но они должны иметь возможность передавать данные, которые они не знают, как обрабатывать. Глядя на netty source at github Кажется, это не так. Так что, вероятно, есть способ сделать это, но я скептически отношусь к его простоте. Попробуйте в любом случае и поделитесь результатами :)   -  person Slartibartfast    schedule 18.10.2011
comment
@Slartibartfast Да, это не просто и требует тяжелой работы :(   -  person Sergii Zagriichuk    schedule 18.10.2011


Ответы (7)


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

person Sergii Zagriichuk    schedule 18.10.2011
comment
Я думаю, что смысл этого потока групп в том, что если вы хотите отправлять несколько типов сообщений, решением будет определить 1 контейнер сообщений общего назначения (скажем, ProtocolMessage) в protobuf, который будет содержать 1 или более различных типов сообщений. Затем вы должны использовать декодер protobuf для типа сообщения ProtocolMessage и оставить любую дальнейшую интерпретацию обработчику сообщений в Netty. - person tmbrggmn; 23.10.2011

Если вы все равно собираетесь писать свои собственные кодеки, возможно, вы захотите рассмотреть реализацию интерфейса Externalizable для пользовательских объектов данных.

  • Serializable не требует больших усилий, но худшая производительность (сериализует все).
  • Protobuf — это хороший компромисс между усилиями и производительностью (требуется поддержка .proto).
  • Externalizable требует больших усилий, но обеспечивает лучшую производительность (нестандартные минимальные кодеки)

Если вы уже знаете, что вашему проекту придется масштабироваться, как горному козлу, возможно, вам придется пройти трудный путь. Protobuf — это не серебряная пуля.

person Dominic Cerisano    schedule 15.11.2011

Теоретически это можно сделать, изменив конвейер для каждого входящего сообщения в соответствии с входящим сообщением. Взгляните на объединение портов. пример в Netty.

Последовательность будет следующей:
1) В декодере кадров или другом "DecoderMappingDecoder" вы проверяете тип входящего сообщения
2) Динамически изменяете конвейер, как показано в примере

Но почему бы не использовать разные подключения и следовать такой последовательности:
1) Добавить другие декодеры в конвейер на основе входящего сообщения только один раз.
2) Добавить тот же экземпляр восходящего канала обработчик в качестве последнего обработчика в конвейере, таким образом, все сообщения направляются в один и тот же экземпляр, что почти похоже на одно соединение.

person Abe    schedule 18.10.2011
comment
Теоретически мы должны модифицировать декодер, чтобы он читал какой-то идентификатор из байтового потока и выбирал правильный алгоритм для декодирования, я думаю об этом решении, но это не простой и не хороший способ сделать это. Почему я хочу открыть только одно соединение, потому что я хочу настроить свой клиент и подготовить соединение с сервером и использовать это (только одно) соединение для отправки ВСЕХ сообщений на сервер, сообщения, такие как операции, и текущая архитектура предоставляют мне возможность создавать много клиентов для каждой операции, я думаю это НЕ ХОРОШАЯ ИДЕЯ! - person Sergii Zagriichuk; 18.10.2011
comment
Если у вас есть n типов сообщений и n разных способов декодирования, вы все равно можете использовать одно и то же соединение с одним MappingDecoder, который проверит тип сообщения и передаст его правильному декодеру для декодирования. Взгляните на встроенный декодер netty (grepcode.com/file/repository.jboss.org/maven2/org.jboss.netty/), который может предложить вам способ использовать декодер без использования конвейера. - person Abe; 18.10.2011

проблема в том, что нет способа отличить два разных сообщения protobuf друг от друга в двоичном формате. Но есть способ решить это в файле protobuf:

message AnyMessage {
    message DataMessage { [...] }
    optional DataMessage dataMessage = 1;
    message TestMessage { [...] }
    optional TestMessage testMessage = 2;
    message SrcMessage { [...] }
    optional SrcMessage srcMessage = 3;
}

необязательные поля, которые не заданы, не создают накладных расходов. Кроме того, вы можете добавить Enum, но это просто бонус.

person Arne    schedule 25.02.2013

Проблема заключается не совсем в ограничении Netty или ограничении кодировщика/декодера. Проблема в том, что буферы протокола Google предлагают только способ сериализации/десериализации объектов, но не предоставляют протокол. У них есть какая-то реализация RPC как часть стандартного дистрибутива, но если вы попытаетесь реализовать их протокол RPC, вы получите 3 уровня косвенности. Что я сделал в одном из проектов, так это определил сообщение, которое в основном представляет собой объединение сообщений. Это сообщение содержит одно поле «Тип» и другое поле, являющееся фактическим сообщением. Вы все равно получите 2 уровня косвенности, а не 3. Таким образом, пример от Netty будет работать для вас, но, как упоминалось в предыдущем посте, вам нужно добавить больше логики в обработчик бизнес-логики.

person devsprint    schedule 05.11.2011

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

person Abhishek kapoor    schedule 18.04.2014

После долгих поисков и страданий... мне пришла в голову идея использовать композицию сообщений в одно сообщение-обертку. Внутри этого сообщения я использую ключ oneof, чтобы ограничить количество разрешенных объектов до единственного. Оформить заказ:

message OneMessage {
    MessageType messageType = 1;

    oneof messageBody {
        Event event = 2;
        Request request  = 3;
        Response response = 4;
    }

    string messageCode = 5; //unique message code
    int64 timestamp = 6; //server time
}
person Ramanqul Buzaubak    schedule 27.04.2017