<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Apache Thrift 跨語言服務(wù)開發(fā)框架

          共 8724字,需瀏覽 18分鐘

           ·

          2019-05-24 04:42

          Apache Thrift 是一種支持多種編程語言的遠程服務(wù)調(diào)用框架,由 Facebook 于 2007 年開發(fā),并于 2008 年進入 Apache 開源項目管理。Apache Thrift 通過 IDL 來定義 RPC 的接口和數(shù)據(jù)類型,然后通過代碼生成工具來生成針對不同編程語言的代碼,目前支持 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml, Delphi?等。

          本文將從 C# 開發(fā)人員的角度介紹基于?Apache Thrift 的服務(wù)開發(fā)過程。

          在文章《開源跨平臺數(shù)據(jù)格式化框架概覽》中主要介紹了各開源框架的數(shù)據(jù)格式化處理部分,但并沒有描述消息的傳輸和 RPC 服務(wù)的定義。而實際上,Apache Thrift 與 Google Protocol Buffers 的一大不同點就是,Google Protocol Buffers 僅支持定義 RPC 服務(wù)接口,而?Apache Thrift 不僅支持定義?RPC 服務(wù)接口,還提供了支持 RPC 服務(wù)實現(xiàn)的完整的堆棧結(jié)構(gòu),并為 RPC 服務(wù)的 Server 端和?Client 端直接生成了可用代碼。如下圖描繪了?Thrift 的堆棧架構(gòu)。

          Clipboard Image.png


          傳輸層(Transport)

          傳輸層提供對網(wǎng)絡(luò) I/O 的抽象,通過 Transport 對客戶端進行抽象,ServerTransport 對服務(wù)端進行抽象。

          • 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     }


          協(xié)議層(Protocol)

          協(xié)議層抽象了數(shù)據(jù)結(jié)構(gòu)的定義,描述了如何組織數(shù)據(jù)以進行傳輸,包括 Encode 和 Decode 數(shù)據(jù)處理。所以,協(xié)議層負責實現(xiàn)數(shù)據(jù)的序列化和反序列化機制,例如序列化 Json, XML, Plain Text, Binary, Compact Binary 等。

          協(xié)議層抽象了大量的讀寫操作接口,以供擴展。


           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 封裝了對輸入輸出流的讀寫操作,輸入輸出流也就代表著協(xié)議層處理的對象。Processor 接口定義的極其簡單。

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

          服務(wù)層(Server)

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

          • 創(chuàng)建一個 Transport;
          • 創(chuàng)建 Transport 使用的?I/O Protocol;
          • 為 I/O Protocol 創(chuàng)建 Processor;
          • 啟動服務(wù),等待客戶端的連接;

          通過抽象 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 的過程:

          • 編寫類似于結(jié)構(gòu)體的消息格式定義,使用類似于 IDL 的語言定義。
          • 使用代碼生成工具,生成目標語言代碼。
          • 在程序中直接使用這些代碼。

          Clipboard Image.png

          這里我們從一個簡單的 Thrift 實例開始,對 Thrift 服務(wù)的構(gòu)建進行直觀的展示。創(chuàng)建一個簡單的 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 中可能發(fā)生的異常類型;
          • service 定義了 CalculatorService 服務(wù)接口;

          Apache Thrift 與 Google Protocol Buffers 的另一個不同點就是,Apache Thrift 支持對 Exception 的定義,使得在定義服務(wù)和實現(xiàn)服務(wù)接口時可以方便的處理服務(wù)端異常。

          在命令行使用 Thrift 代碼生成工具為 C# 編程語言生成代碼:

          thrift --gen csharp calculator.thrift

          代碼生成工具根據(jù)?calculator.thrift 中的定義會生成 3 個 C# 代碼文件:

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

          有了這些生成的代碼文件,就可以設(shè)計服務(wù)端和客戶端代碼了。這里,創(chuàng)建 3 個 solution 文件:

          • Contracts:存放生成的代碼文件,共享給 Server 和 Client;
          • Server:實現(xiàn)服務(wù)端代碼,為客戶端提供服務(wù);
          • Client:實現(xiàn)客戶端代碼,調(diào)用服務(wù)端;

          Clipboard Image.png


          Contracts 代碼

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

          namespace csharp Contracts

          所以生成的代碼的 namespace 即為 Contracts。


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


          相應(yīng)的,在?CalculatorService 文件中也生成了名為 Iface 的接口定義,也就是 Server 端需要為 Client 端實現(xiàn)的接口。

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

          Server 端實現(xiàn)

          為了實現(xiàn) CalculatorService,需要實現(xiàn)一個 CalculatorServiceHandler 類來實現(xiàn)生成的 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 段,判斷了當除數(shù)為 0 時將拋出?Contracts.DivideByZeroException 異常。

          然后,需要啟動 Server 來提供 CalculatorService 服務(wù)。將?CalculatorServiceHandler 類的實例傳遞給 CalculatorService.Processor 的構(gòu)造函數(shù),指定 Socket 綁定端口 8888,然后啟動服務(wù)。


           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 端實現(xiàn)

          Client 端消費 Server 端的代碼更加簡單,基本上 Thrift 都已提供了默認的實現(xiàn),需要做的就是指定地址、端口和協(xié)議。


           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 端程序,實現(xiàn)簡單的服務(wù)調(diào)用了。



          作者:匠心十年

          出處:https://www.cnblogs.com/gaochundong/

          原文鏈接:https://www.cnblogs.com/gaochundong/p/apache_thrift.html

          本文轉(zhuǎn)自博客園網(wǎng),版權(quán)歸原作者所有。

          瀏覽 156
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  在线观看一区二区三区四区五区 | 99久久精品国产毛片 | 无码无码一区 | 婷婷最新网址 | 午夜三级免费福利影院 |