Netty + ProtoBuffer:一次连接的几个通信消息

17

在阅读Netty教程时,我发现一个简单的描述如何集成Netty和Google Protocol Buffers。我开始研究该示例(因为文档中没有更多信息),并编写了一个类似于本地时间应用程序的简单应用程序。但是该示例在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 class 只能创建一个工厂(pipeline factory),即 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()));

或者其他技术吗? 非常感谢。


您可以在管道中添加许多解码器/编码器,但它们应该能够传递它们不知道如何处理的数据。查看GitHub上的netty源代码,似乎并非如此。因此可能有一种方法来解决这个问题,但我对其是否如此简单持怀疑态度。无论如何,请尝试并分享结果 :) - Slartibartfast
@Slartibartfast 是的,这并不简单,需要努力工作 :( - Sergii Zagriichuk
7个回答

6

我在谷歌群组中找到了netty作者的帖子,并理解了我必须改变我的架构或编写自己的解码器,就像我之前所写的那样,所以开始考虑哪种方式会更容易和更好。


4
我认为该组讨论的重点是,如果您想发送多种类型的消息,一个解决方案是定义一个通用的“消息容器”(假设为ProtocolMessage),其中包含一种或多种不同类型的消息。然后,您将使用Protobuf的解码器来解码ProtocolMessage消息类型,并将进一步的解释留给Netty中的消息处理程序。 - tmbrggmn

3

如果你打算编写自己的编解码器,可以考虑为定制数据对象实现Externalizable接口。

  • Serializable很简单,但性能最差(序列化所有内容)。
  • Protobuf在努力和性能之间取得了平衡(需要.proto文件维护)
  • Externalizable的工作量很大,但性能最佳(自定义最小编解码器)

如果你已经知道你的项目要像山羊一样快速增长,你可能必须走艰难的路。 Protobuf并不是万无一失的。


2
理论上,可以通过修改管道来适应每个传入消息来实现此操作。查看Netty中的端口统一示例。
步骤如下: 1)在帧解码器或另一个“DecoderMappingDecoder”中检查传入消息的消息类型。 2)根据示例动态修改管道。
但是,为什么不使用不同的连接并遵循以下顺序: 1)根据传入消息,在管道中添加其他解码器。 2)将相同的通道上行处理程序添加为管道中的最后一个处理程序,这样所有消息都会路由到同一个实例,几乎就像拥有单个连接。

理论上,我们应该修改解码器以从字节流中读取某些ID并选择正确的解码算法。我考虑过这个解决方案,但这不是一个简单或好的方法。为什么我只想打开一个连接,因为我想设置我的客户端并准备连接到服务器,并使用这个(只有一个)连接将所有消息发送到服务器,例如操作和当前架构为我提供了为每个操作创建许多客户端的可能性,我认为这不是一个好主意! - Sergii Zagriichuk
如果您有n种消息类型和n种不同的解码方式,那么您仍然可以使用相同的连接,该连接具有单个“MappingDecoder”,它将检查消息类型并将其传递给正确的解码器进行解码。请查看netty嵌入式解码器(http://grepcode.com/file/repository.jboss.org/maven2/org.jboss.netty/netty/3.1.4.GA/org/jboss/netty/handler/codec/embedder/DecoderEmbedder.java),它可能为您提供一种使用解码器而不使用管道的方法。 - Abe

2

问题在于在二进制格式中无法区分两个不同的protobuf消息。但是可以在protobuf文件中解决这个问题:

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

未设置的可选字段不会产生额外开销。此外,您可以添加一个枚举,但这只是一个额外的奖励。


1
问题并不完全是 Netty 或编码器/解码器的限制。问题在于 Google Protocol Buffers 只提供了一种序列化/反序列化对象的方法,但并没有提供协议。它们作为标准分发的一部分有一种RPC实现,但如果您尝试实现他们的RPC协议,那么你将会最终得到3个间接层。 在我做的项目中,我定义了一个基本上是消息联合体的消息。此消息包含一个类型字段和另一个实际消息字段。你仍然会有2个间接层,但不是3个。这样,Netty中的示例将对您起作用,但正如之前的帖子中提到的那样,您必须在业务逻辑处理程序中放置更多逻辑。

0

您可以使用消息隧道将各种类型的消息作为负载发送到单个消息中。希望这有所帮助。


0

经过长时间的研究和苦思冥想... 我想到了使用消息组合成一个包装消息的方法。在该消息内部,我使用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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接