使用Camel-Netty4反序列化对象

时间:2015-02-20 17:22:27

标签: sockets apache-camel deserialization netty decoder

在客户端,我有一个工作正常的套接字发送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>

但我的对象超出帧长度max,抛出异常 -

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解码为详细信息),但根本没有幸运。

有谁知道如何实现这一目标?我只需要收到一个简单的对象,不应该那么难,应该是吗?

1 个答案:

答案 0 :(得分:0)

好吧,经过一番努力,我想出了一个解决方法:

我决定使用 ReplayingDecoder 代替ObjectDecoderByteToMessageDecoder,因为:

  1. ObjectDecoderObjectOutputStreamquote
  2. 不兼容
  3. ByteToMessaDecoder
    • 在执行实际读取操作之前,您需要检查输入ByteBuf上是否有足够的字节可供读取。 (引自Netty in Action)
    • 应通过添加另一个解码器在流水线中更早地处理帧检测。 (quote
    • 如果未释放返回的缓冲区或将其添加到out列表中,某些方法(如ByteBuf.readBytes(int))将导致内存泄漏。 (quote
    • 最后因为与ReplayingDecoder相比,它在测试期间的平均时间更短。也许是因为它使用了ByteBuf
  4. ReplayingDecoder也有一些限制(均来自Netty in Action):

    1. 并非支持ByteBuf上的所有操作,如果您调用不受支持的操作,则会抛出UnreplayableOperationException
    2. ByteBuf.readableBytes()大部分时间都不会回复你的期望。
    3. 因为我不知道消息的结尾是什么样的(如果我认为它适合DelimiterBasedFrameDecoder),也不知道每个到达包的长度(as it's said in this response),那么我最后将字节分组,做这样的事情:

      // 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){}
          }
      }
      

      TODO

      • 我可能需要比org.apache.commons.lang.SerializationUtils更好的反序列化
      • 如果反序列化的对象不是我需要的类型,该怎么办?我怎么丢弃它?

      希望这可以帮助别人!