Десериализовать объект с помощью Camel-Netty4

На стороне клиента у меня есть исправный сокет, отправляющий объект Java:

Detail detail = new Detail(); //client object
Socket client = new Socket(host, port);
OutputStream out = client.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(detail);
oos.flush();

Примечание: это клиентский сокет, поэтому его нельзя изменить.

На стороне сервера у меня есть camel-2.14.1 + Spring + netty4, работающий в Jboss AS7, который имеет этот простой пользовательский рут:

from("netty4:tcp://0.0.0.0:6549?"+ 
     "keepAlive=true&sync=true&decoder=#detailDecoder&encoder=#detailEncoder")
  .log("server recive: ${body.getArea}")
  .processRef("someDetailProcessor") //loaded with Spring
  .log("response [${body.getArea}]");

Я уже понял, что не могу использовать StringDecoder/StringEncoder для десериализации / сериализации объектов, потому что эти кодировщики ждут более текстовых сообщений.

По этой причине я использовал ObjectDecoder/ObjectEncoder, вводя их вот так:

<bean id="objDecoder" class="org.apache.camel.component.netty4.ChannelHandlerFactories" factory-method="newObjectDecoder">
    <constructor-arg name="protocol" value="tcp"/>
</bean>
<bean id="objEncoder" class="org.apache.camel.component.netty4.ChannelHandlerFactories" factory-method="newObjectEncoder">
    <constructor-arg name="protocol" value="tcp"/>
</bean>

Но мой объект превышает максимальную длину кадра, вызывая исключение -

Closing channel as an exception was thrown from Netty. Caused by: [io.netty.handler.codec.TooLongFrameException - Adjusted frame length exceeds 1048576: 2901213193 - discarded]

Я попытался настроить LengthFieldBasedFrameDecoder (который является суперклассом ObjectDecoder и также ожидает целочисленное поле заголовка, которое представляет длину тела сообщения, поэтому бесполезно). Я также использовал ByteToMessageDocoder по-разному (создав свой собственный класс и пытаясь декодировать ByteBuf в деталь), но без особого успеха.

Кто-нибудь знает, как этого добиться? Мне просто нужно получить простой предмет, не должно быть так сложно, не так ли?


person robert    schedule 20.02.2015    source источник
comment
Что ж, читая больше документации, которую я нашел на ObjectDecoder: обратите внимание, что сериализованная форма, которую ожидает этот декодер, несовместима со стандартом ObjectOutputStream. Пожалуйста, используйте ObjectEncoder или ObjectEncoderOutputStream, чтобы обеспечить совместимость с этим декодером ...... Я использую ObjectOutputStream для отправки объекта :( аааааааааааааааааааааааааааааааааааа более там я почти готов !!   -  person robert    schedule 20.02.2015


Ответы (1)


Что ж, немного поработав с этим, я придумал одно обходное решение:

Я решил использовать ReplayingDecoder вместо ObjectDecoder и ByteToMessageDecoder, потому что:

  1. ObjectDecoder несовместимо с ObjectOutputStream (цитата )
  2. ByteToMessaDecoder
    • You need to check if there are enough bytes ready to read on the input ByteBuf before you do the actual read operation. (quote from Netty in Action)
    • Обнаружение кадра следует обрабатывать раньше в конвейере, добавляя еще один декодер. (цитата)
    • Некоторые методы, такие как ByteBuf.readBytes(int), вызовут утечку памяти, если возвращаемый буфер не будет освобожден или добавлен в список out. (цитата)
    • И наконец, потому что он дал мне худшее среднее время во время тестирования по сравнению с ReplayingDecoder. Может быть, из-за использования ByteBuf.

ReplayingDecoder также имеет некоторые ограничения (оба цитируются из Netty in Action):

  1. Не все операции на ByteBuf поддерживаются, и если вы вызовете неподдерживаемую операцию, она выдаст UnreplayableOperationException.
  2. ByteBuf.readableBytes() большую часть времени не вернет то, что вы ожидаете.

Так как я не знаю, как выглядит конец сообщений (если бы это было подходящим DelimiterBasedFrameDecoder), ни длина каждого пакета прибытия (как сказано в этом ответе), затем я сгруппировал байты, сделав что-то вроде этого:

// to collect the object bytes
private ByteArrayOutputStream baos = new ByteArrayOutputStream(); 

@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {

    if(in.isReadable()){

        // wrIdx - rdIdx instead of .readableBytes()
        byte[] frame = new byte[in.writerIndex()-in.readerIndex()];

        // I read just what I get, so the signal is never thrown
        in.readBytes(frame);

        // collecting bytes
        baos.write(frame);

        // it'll achieve this only when all the bytes from
        // the incoming object have been collected
        try{
            out.add(SerializationUtils.deserialize(baos.toByteArray()));
        }
        catch(Exception e){}
    }
}

ЗАДАЧИ:

  • Мне может понадобиться что-то получше, чем org.apache.commons.lang.SerializationUtils, для десериализации
  • что, если десериализованный объект не того типа, который мне нужен. Как мне его отказаться?

Надеюсь, это может кому-то помочь!

person robert    schedule 24.02.2015