Apache Thrift 跨语言服务开发框架

简介:

Apache Thrift 是一种支持多种编程语言的远程服务调用框架,由 Facebook 于 2007 年开发,并于 2008 年进入 Apache 开源项目管理。Apache Thrift 通过 IDL 来定义 RPC 的接口和数据类型,然后通过代码生成工具来生成针对不同编程语言的代码,目前支持 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml, Delphi 等。

本文将从 C# 开发人员的角度介绍基于 Apache Thrift 的服务开发过程。

在文章《开源跨平台数据格式化框架概览》中主要介绍了各开源框架的数据格式化处理部分,但并没有描述消息的传输和 RPC 服务的定义。而实际上,Apache Thrift 与 Google Protocol Buffers 的一大不同点就是,Google Protocol Buffers 仅支持定义 RPC 服务接口,而 Apache Thrift 不仅支持定义 RPC 服务接口,还提供了支持 RPC 服务实现的完整的堆栈结构,并为 RPC 服务的 Server 端和 Client 端直接生成了可用代码。如下图描绘了 Thrift 的堆栈架构

传输层(Transport)

传输层提供对网络 I/O 的抽象,通过 Transport 对客户端进行抽象,ServerTransport 对服务端进行抽象。

  • TTransport
    • TBufferedTransport
    • TFramedTransport
    • TStreamTransport
      • TSocket
      • TTLSSocket
    • THttpClient
    • TMemoryBuffer
    • TNamedPipeClientTransport
  • TServerTransport
    • TServerSocket
    • TTLSServerSocket
    • TNamedPipeServerTransport

其实,看一眼 TSocket 的源代码就可以了解事情的真相了。

复制代码
 1     public TSocket(string host, int port, int timeout)
 2     {
 3       this.host = host;
 4       this.port = port;
 5       this.timeout = timeout;
 6 
 7       InitSocket();
 8     }
 9 
10     private void InitSocket()
11     {
12       client = new TcpClient();
13       client.ReceiveTimeout = client.SendTimeout = timeout;
14       client.Client.NoDelay = true;
15     }
复制代码

协议层(Protocol)

协议层抽象了数据结构的定义,描述了如何组织数据以进行传输,包括 Encode 和 Decode 数据处理。所以,协议层负责实现数据的序列化和反序列化机制,例如序列化 Json, XML, Plain Text, Binary, Compact Binary 等。

协议层抽象了大量的读写操作接口,以供扩展。

复制代码
 1   public abstract void WriteMessageBegin(TMessage message);
 2   public abstract void WriteMessageEnd();
 3   public abstract void WriteStructBegin(TStruct struc);
 4   public abstract void WriteStructEnd();
 5   public abstract void WriteFieldBegin(TField field);
 6   public abstract void WriteFieldEnd();
 7   public abstract void WriteFieldStop();
 8   public abstract void WriteMapBegin(TMap map);
 9   public abstract void WriteMapEnd();
10   public abstract void WriteListBegin(TList list);
11   public abstract void WriteListEnd();
12   public abstract void WriteSetBegin(TSet set);
13   public abstract void WriteSetEnd();
14   public abstract void WriteBool(bool b);
15   public abstract void WriteByte(sbyte b);
16   public abstract void WriteI16(short i16);
17   public abstract void WriteI32(int i32);
18   public abstract void WriteI64(long i64);
19   public abstract void WriteDouble(double d);
20   public virtual void WriteString(string s);
21   public abstract void WriteBinary(byte[] b);
22   
23   public abstract TMessage ReadMessageBegin();
24   public abstract void ReadMessageEnd();
25   public abstract TStruct ReadStructBegin();
26   public abstract void ReadStructEnd();
27   public abstract TField ReadFieldBegin();
28   public abstract void ReadFieldEnd();
29   public abstract TMap ReadMapBegin();
30   public abstract void ReadMapEnd();
31   public abstract TList ReadListBegin();
32   public abstract void ReadListEnd();
33   public abstract TSet ReadSetBegin();
34   public abstract void ReadSetEnd();
35   public abstract bool ReadBool();
36   public abstract sbyte ReadByte();
37   public abstract short ReadI16();
38   public abstract int ReadI32();
39   public abstract long ReadI64();
40   public abstract double ReadDouble();
41   public virtual string ReadString();
42   public abstract byte[] ReadBinary();
复制代码

处理层(Processor)

Processor 封装了对输入输出流的读写操作,输入输出流也就代表着协议层处理的对象。Processor 接口定义的极其简单。

  public interface TProcessor
  {
    bool Process(TProtocol iprot, TProtocol oprot);
  }

服务层(Server)

Server 将所有功能整合到一起:

  • 创建一个 Transport;
  • 创建 Transport 使用的 I/O Protocol;
  • 为 I/O Protocol 创建 Processor;
  • 启动服务,等待客户端的连接;

通过抽象 TServer 类来提供上述整合。

  • TServer
    • TSimpleServer -- Simple single-threaded server for testing.
    • TThreadedServer -- Server that uses C# threads (as opposed to the ThreadPool) when handling requests.
    • TThreadPoolServer -- Server that uses C# built-in ThreadPool to spawn threads when handling requests.
复制代码
 1     public TServer(TProcessor processor,
 2               TServerTransport serverTransport,
 3               TTransportFactory inputTransportFactory,
 4               TTransportFactory outputTransportFactory,
 5               TProtocolFactory inputProtocolFactory,
 6               TProtocolFactory outputProtocolFactory,
 7               LogDelegate logDelegate)
 8     {
 9     }
10 
11     public abstract void Serve();
12     public abstract void Stop();
复制代码

Thrift 实例

使用 Thrift 的过程:

  • 编写类似于结构体的消息格式定义,使用类似于 IDL 的语言定义。
  • 使用代码生成工具,生成目标语言代码。
  • 在程序中直接使用这些代码。

这里我们从一个简单的 Thrift 实例开始,对 Thrift 服务的构建进行直观的展示。创建一个简单的 CalculatorService,通过 Calculate 接口来支持 "+ - x /" 简单的计算。Thrift 文件名为 calculator.thrift。

复制代码
namespace cpp com.contracts.calculator
namespace java com.contracts.calculator
namespace csharp Contracts

enum Operation {
  Add = 1,
  Subtract = 2,
  Multiply = 3,
  Divide = 4
}

exception DivideByZeroException {
  1: string Message;
}

service CalculatorService {
   i32 Calculate(1:i32 x, 2:i32 y, 3:Operation op) throws (1:DivideByZeroException divisionByZero);
}
复制代码

上面的 calculator.thrift 实例中,

  • namespace 定义针对不同编程语言的名空间或者包;
  • enum 定义了 Calculate 需要支持的枚举类型;
  • exception 定义了 Calculate 中可能发生的异常类型;
  • service 定义了 CalculatorService 服务接口;

Apache Thrift 与 Google Protocol Buffers 的另一个不同点就是,Apache Thrift 支持对 Exception 的定义,使得在定义服务和实现服务接口时可以方便的处理服务端异常。

在命令行使用 Thrift 代码生成工具为 C# 编程语言生成代码:

thrift --gen csharp calculator.thrift

代码生成工具根据 calculator.thrift 中的定义会生成 3 个 C# 代码文件:

  • CalculatorService.cs
  • DivideByZeroException.cs
  • Operation.cs

有了这些生成的代码文件,就可以设计服务端和客户端代码了。这里,创建 3 个 solution 文件:

  • Contracts:存放生成的代码文件,共享给 Server 和 Client;
  • Server:实现服务端代码,为客户端提供服务;
  • Client:实现客户端代码,调用服务端;

Contracts 代码

由于在 calculator.thrift 文件中定义了 C# 的名空间:

namespace csharp Contracts

所以生成的代码的 namespace 即为 Contracts。

复制代码
namespace Contracts
{
  public enum Operation
  {
    Add = 1,
    Subtract = 2,
    Multiply = 3,
    Divide = 4,
  }
}
复制代码

相应的,在 CalculatorService 文件中也生成了名为 Iface 的接口定义,也就是 Server 端需要为 Client 端实现的接口。

  public partial class CalculatorService {
    public interface Iface {
      int Calculate(int x, int y, Operation op);
    }
  }

Server 端实现

为了实现 CalculatorService,需要实现一个 CalculatorServiceHandler 类来实现生成的 Contracts 中的 CalculatorService.Iface 接口。

复制代码
 1   public class CalculatorServiceHandler : CalculatorService.Iface
 2   {
 3     public int Calculate(int x, int y, Operation op)
 4     {
 5       switch (op)
 6       {
 7         case Operation.Add:
 8           return x + y;
 9         case Operation.Subtract:
10           return x - y;
11         case Operation.Multiply:
12           return x * y;
13         case Operation.Divide:
14           if (y == 0)
15             throw new Contracts.DivideByZeroException()
16             {
17               Message = "Cannot divide by zero."
18             };
19           return x / y;
20       }
21 
22       throw new NotImplementedException();
23     }
24   }
复制代码

上面代码中的 Operation.Divide 段,判断了当除数为 0 时将抛出 Contracts.DivideByZeroException 异常。

然后,需要启动 Server 来提供 CalculatorService 服务。将 CalculatorServiceHandler 类的实例传递给 CalculatorService.Processor 的构造函数,指定 Socket 绑定端口 8888,然后启动服务。

复制代码
 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       var handler = new CalculatorServiceHandler();
 6       var processor = new CalculatorService.Processor(handler);
 7 
 8       TServerTransport transport = new TServerSocket(8888);
 9       TServer server = new TThreadPoolServer(processor, transport);
10 
11       server.Serve();
12 
13       Console.ReadKey();
14     }
15   }
复制代码

Client 端实现

Client 端消费 Server 端的代码更加简单,基本上 Thrift 都已提供了默认的实现,需要做的就是指定地址、端口和协议。

复制代码
 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       var transport = new TSocket("localhost", 8888);
 6       var protocol = new TBinaryProtocol(transport);
 7       var client = new CalculatorService.Client(protocol);
 8 
 9       transport.Open();
10 
11       var test1 = client.Calculate(100, 2, Operation.Add);
12       Console.WriteLine(test1);
13 
14       var test2 = client.Calculate(100, 2, Operation.Subtract);
15       Console.WriteLine(test2);
16 
17       var test3 = client.Calculate(100, 2, Operation.Multiply);
18       Console.WriteLine(test3);
19 
20       var test4 = client.Calculate(100, 2, Operation.Divide);
21       Console.WriteLine(test4);
22 
23       try
24       {
25         var test5 = client.Calculate(100, 0, Operation.Divide);
26         Console.WriteLine(test5);
27       }
28       catch (Contracts.DivideByZeroException ex)
29       {
30         Console.WriteLine(ex.Message);
31       }
32 
33       Console.ReadKey();
34     }
35   }
复制代码

然后,就可以启动 Server 端和 Client 端程序,实现简单的服务调用了。








本文转自匠心十年博客园博客,原文链接:http://www.cnblogs.com/gaochundong/p/apache_thrift.html,如需转载请自行联系原作者

目录
相关文章
|
11月前
|
人工智能 运维 Java
Flink Agents:基于Apache Flink的事件驱动AI智能体框架
本文基于Apache Flink PMC成员宋辛童在Community Over Code Asia 2025的演讲,深入解析Flink Agents项目的技术背景、架构设计与应用场景。该项目聚焦事件驱动型AI智能体,结合Flink的实时处理能力,推动AI在工业场景中的工程化落地,涵盖智能运维、直播分析等典型应用,展现其在AI发展第四层次——智能体AI中的重要意义。
3497 27
Flink Agents:基于Apache Flink的事件驱动AI智能体框架
|
存储 缓存 算法
分布式锁服务深度解析:以Apache Flink的Checkpointing机制为例
【10月更文挑战第7天】在分布式系统中,多个进程或节点可能需要同时访问和操作共享资源。为了确保数据的一致性和系统的稳定性,我们需要一种机制来协调这些进程或节点的访问,避免并发冲突和竞态条件。分布式锁服务正是为此而生的一种解决方案。它通过在网络环境中实现锁机制,确保同一时间只有一个进程或节点能够访问和操作共享资源。
711 3
|
Linux 网络安全 Apache
CentOS 7.2配置Apache服务httpd(上)
CentOS 7.2配置Apache服务httpd(上)
1109 1
|
消息中间件 存储 数据采集
Apache InLong:构建10万亿级数据管道的全场景集成框架
Apache InLong(应龙)是一站式、全场景海量数据集成框架,支持数据接入、同步与订阅,具备自动、安全、可靠和高性能的数据传输能力。源自腾讯大数据团队,现为 Apache 顶级项目,广泛应用于广告、支付、社交等多个领域,助力企业构建高效数据分析与应用体系。
|
Java Apache C++
别再手写RPC了,Apache Thrift帮你自动生成RPC客户端及服务端代码
Thrift 是一个轻量级、跨语言的远程服务调用框架,由 Facebook 开发并贡献给 Apache。它通过 IDL 生成多种语言的 RPC 服务端和客户端代码,支持 C++、Java、Python 等。Thrift 的主要特点包括开发速度快、接口维护简单、学习成本低和多语言支持。广泛应用于 Cassandra、Hadoop 等开源项目及 Facebook、百度等公司。
别再手写RPC了,Apache Thrift帮你自动生成RPC客户端及服务端代码
|
存储 分布式计算 druid
大数据-155 Apache Druid 架构与原理详解 数据存储 索引服务 压缩机制
大数据-155 Apache Druid 架构与原理详解 数据存储 索引服务 压缩机制
395 3
|
存储 数据挖掘 数据处理
Apache Paimon 是一款高性能的数据湖框架,支持流式和批处理,适用于实时数据分析
【10月更文挑战第8天】随着数据湖技术的发展,越来越多企业开始利用这一技术优化数据处理。Apache Paimon 是一款高性能的数据湖框架,支持流式和批处理,适用于实时数据分析。本文分享了巴别时代在构建基于 Paimon 的 Streaming Lakehouse 的探索和实践经验,包括示例代码和实际应用中的优势与挑战。
973 1
|
Linux PHP Apache
CentOS 7.2配置Apache服务httpd(下)
CentOS 7.2配置Apache服务httpd(下)
369 1
|
前端开发 JavaScript Java
Apache Wicket 框架:踏上从新手到英雄的逆袭之路,成就你的编程传奇!
【9月更文挑战第4天】Apache Wicket是一款基于Java的开源Web应用框架,以简洁、易维护及强大功能著称。它采用组件化设计,让页面开发更为模块化。Wicket的简洁编程模型、丰富的组件库、良好的可维护性以及对Ajax的支持,使其成为高效开发Web应用的理想选择。下文将通过解析Wicket的基本概念与特性,帮助读者深入了解这一框架的优势。
848 1
|
Ubuntu Linux 测试技术
在Linux中,已知 apache 服务的访问日志按天记录在服务器本地目录/app/logs 下,由于磁盘空间紧张现在要求只能保留最近7天的访问日志,请问如何解决?
在Linux中,已知 apache 服务的访问日志按天记录在服务器本地目录/app/logs 下,由于磁盘空间紧张现在要求只能保留最近7天的访问日志,请问如何解决?

推荐镜像

更多