如何使用Thrift而不是RPC进行消息传递

时间:2018-08-31 22:20:21

标签: c .net-core thrift

我知道Thrift主要针对成熟的客户端-服务器RPC,但是从高级架构的角度来看,它似乎也可以完美地用于双向消息传递。

我想在两端(C,.NET Core)上构建的内容如下:

  • 接收方法:具有对套接字的引用,读取完整的消息,对其进行反序列化,然后将其返回
  • 发送方法:引用套接字,序列化给定消息,然后将其发送到线路。

我不需要线程服务器,……任何花哨的东西。从本质上讲,我想超越例如Protobuffs提供了开箱即用的处理功能,可以在接收端缓冲整个消息,通常还可以缓存消息。

问题是我找不到关于如何使用当前库(我个人对.NET Core和C语言的API)进行构建的文档。我唯一发现的是这个question,但实际上并没有指向任何资源。

2 个答案:

答案 0 :(得分:1)

Thrift是一个RPC和序列化框架。这意味着,您也可以仅使用序列化部分而不使用RPC。

与消息传递系统结合使用时,通常(大致)如下:

  • 将消息序列化到缓冲区
  • 通过您想要的任何方式发送该缓冲区
  • 接收端反序列化缓冲区并处理数据

如果您打算通过同一通道发送不同类型的消息,则最好使用一个union信封结构来包含所有可能的消息正文:

 struct MessageOne {
      // contents of this message type
 }

 struct MessageTwo {
      // contents of this message type
 }

 struct MessageThree {
      // contents of this message type
 }

 union MyMessageEnvelope {
      1: MessageOne   one
      2: MessageTwo   two
      3: MessageThree  three
      // easily extendable 
 }

为使其更美观/可重用,还可以实现一种自定义传输来满足需要并进一步封装逻辑。 Thrift的模块化结构使其变得容易(您所链接的帖子也对此进行了引用)。源树的/contrib文件夹中有一些示例可以用作起点。

如果您完全不知道从哪里开始:先看一下教程,再看一下测试套件程序,它们都是Thrift入门者的学习资源。

答案 1 :(得分:1)

做一些非常类似的事的笔记:

  • 同时使用C#(。Net Core和Framework的混合)和C ++的客户端
  • 使用Thrift RPC以及“纯消息”进行发布/订阅和常规序列化

The suggestion to put all messages in a top-level union是一个很好的选择,因为它将使反序列化消息变得更加容易。

给出以下节约条件:

struct SubscribeRequest {
    1: string topic,
    2: string appid,
}

struct SubscribeReply {
    1: bool success,
    2: string topic,
}
service HttpService {
    HttpSDKDataTypes.SubscribeReply Subscribe(1: HttpSDKDataTypes.SubscribeRequest message),
}

thrift -gen netcore为您提供:

  public async Task<Ruyi.SDK.Http.SubscribeReply> SubscribeAsync(Ruyi.SDK.Http.SubscribeRequest message, CancellationToken cancellationToken)
  {
    await OutputProtocol.WriteMessageBeginAsync(new TMessage("Subscribe", TMessageType.Call, SeqId), cancellationToken);

消息标识符包含在RPC调用中。如果您不使用RPC调用,则会得到“原始”结构,而没有指示如何反序列化它们。

将它们放入union

union UnionExample {
    1: SubscribeRequest request,
    2: SubscribeReply reply,    
}

为您处理:

public async Task WriteAsync(TProtocol oprot, CancellationToken cancellationToken)
{
  oprot.IncrementRecursionDepth();
  try
  {
    var struc = new TStruct("SubscribeRequest");
    await oprot.WriteStructBeginAsync(struc, cancellationToken);
    var field = new TField();
    if (Topic != null && __isset.topic)
    {
      field.Name = "topic";
      field.Type = TType.String;
      field.ID = 1;

反序列化后,可以使用以下方法处理它们:

    public void handler(UnionExample example)
    {
        if (example.__isset.request)
        {
            SubscribeRequest msg = example.request;
            // ...
        }
        else if (example.__isset.reply)
        {
            SubscribeReply msg = example.reply;
            // ...
        }

检查生成器中可用的选项。 thrift -gen "csharp:union,async"让您使用pattern matching

   public void handler(UnionExample example)
    {
        switch (example.Data)
        {
            case SubscribeRequest msg:
                //...
            case SubscribeReply msg:
                //...

不幸的是,netcore生成器在0.11.0中没有这样做。

节俭的github存储库具有examples of serializing into memory(而不是使用RPC)。一般来说,它类似于:

Stream stm = new MemoryStream();
TTransport trans = new TStreamTransport(null, stm);
TProtocol prot = new TJSONProtocol(trans);

如果要创建很多MemoryStream实例,请查看Microsoft.IO.RecycableMemoryStream

使用自定义协议/传输将简化发送消息的过程,因为它将处理样板(并避免先将多余的对象序列化到内存,然后再对其进行处理)。已经提到了thrift contrib/ folder。  Here's our C# example使用Thrift over ZeroMQ。

最后的笔记。如果您完全要使用RPC功能,请使用单个struct参数编写服务方法。含义:

service HttpService {
    // Do this
    string Subscribe(1: SubscribeRequest message),
    // Not this
    string Subscribe(1: string topic, 2: string appid,),
}

这将使摆脱RPC和/或重用消息更加容易。