官方网站
Apache软件框架,用于可扩展的跨语言服务开发,将软件栈和代码引擎组合起来,能够使C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi无缝地高效地工作。Thrift是一个典型的客户端/服务端结构,都可以使用不同的语言开发。

入门

  • 下载Apache Thrift
    下载链接,可以在项目中使用gradle导入,暂时不用管
  • 下载thrift编译器
    installing Thrift 选择window安装,然而我是使用 -- scoop安装的
    scoop search thrift
    scoop install thrift
  • 写一个.thrift结尾的文件
    当安装完thrift编译器后,就需要编写一个thrift文件,这个文件是由thrift类型和services组成的接口定义,你在文件中定义的services会在服务端体现和在客户端响应。thrift编译器是将你编写的thrift文件转化为符合客户端与服务端的源代码。编译器生成格式如下:
    thrift --gen <language> <Thrift filename>

    更多关于thrift可以读 -- 白皮书

例子
Apache thrift使用数据类型和service接口来简单定义一个thrift文件。Thrift编译器可以很容易地build 那些跨语言编译的prc客户端和服务端。与那些按模板编写好代码去序列化传输对象和远程调用方法不同,你可以按照一下内容去business

下面是一个简单的存储(store)来自web的对象数据。

thrift文件

/**
 * Ahh, now onto the cool part, defining a service. Services just need a name
 * and can optionally inherit from another service using the extends keyword.
 */
service Calculator extends shared.SharedService {

  /**
   * A method definition looks like C code. It has a return type, arguments,
   * and optionally a list of exceptions that it may throw. Note that argument
   * lists and exception lists are specified using the exact same syntax as
   * field lists in struct or exception definitions.
   */

   void ping(),

   i32 add(1:i32 num1, 2:i32 num2),

   i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),

   /**
    * This method has a oneway modifier. That means the client only makes
    * a request and does not listen for any response at all. Oneway methods
    * must be void.

python 编写的客户端

def main():
    # Make socket
    transport = TSocket.TSocket('localhost', 9090)

    # Buffering is critical. Raw sockets are very slow
    transport = TTransport.TBufferedTransport(transport)

    # Wrap in a protocol
    protocol = TBinaryProtocol.TBinaryProtocol(transport)

    # Create a client to use the protocol encoder
    client = Calculator.Client(protocol)

    # Connect!
    transport.open()

    client.ping()
    print('ping()')

    sum_ = client.add(1, 1)

java编写的服务端

//init
 try {
      TServerTransport serverTransport = new TServerSocket(9090);
      TServer server = new TSimpleServer(new Args(serverTransport).processor(processor));

      // Use this for a multithreaded server
      // TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).processor(processor));

      System.out.println("Starting the simple server...");
      server.serve();
    } catch (Exception e) {
      e.printStackTrace();
    }

//handler
public class CalculatorHandler implements Calculator.Iface {

  private HashMap<Integer,SharedStruct> log;

  public CalculatorHandler() {
    log = new HashMap<Integer, SharedStruct>();
  }

  public void ping() {
    System.out.println("ping()");
  }

  public int add(int n1, int n2) {
    System.out.println("add(" + n1 + "," + n2 + ")");
    return n1 + n2;
  }

  public int calculate(int logid, Work work) throws InvalidOperation {
    System.out.println("calculate(" + logid + ", {" + work.op + "," + work.num1 + "," + work.num2 + "})");
    int val = 0;
    switch (work.op) {
    case ADD:
      val = work.num1 + work.num2;
      break;
    case SUBTRACT:
      val = work.num1 - work.num2;
      break;
    case MULTIPLY:
      val = work.num1 * work.num2;
      break;
    case DIVIDE:
      if (work.num2 == 0) {
        InvalidOperation io = new InvalidOperation();
        io.whatOp = work.op.getValue();
        io.why = "Cannot divide by 0";
        throw io;
      }
      val = work.num1 / work.num2;
      break;
    default:
      InvalidOperation io = new InvalidOperation();
      io.whatOp = work.op.getValue();
      io.why = "Unknown operation";
      throw io;
    }

    SharedStruct entry = new SharedStruct();
    entry.key = logid;
    entry.value = Integer.toString(val);
    log.put(logid, entry);

    return val;
  }

  public SharedStruct getStruct(int key) {
    System.out.println("getStruct(" + key + ")");
    return log.get(key);
  }

  public void zip() {
    System.out.println("zip()");
  }

}

代码具体是说明意思,可以先放一边,从服务端代码可以看出跟之前protobuf的格式差不多(init,handler)。但是跟protobuf比较而言的话,明显thrift支持的语言更多,而且thrift是由Facebook捐献给apache的顶级开源项目,是否比Google的protobuf更好用或者更好优势呢 xxxx

thrift结构类型

Thrift types
Thrift的类型能够最大限度满足开发者使用,不管使用那个语言进行开发。Thrift接口描述语言(IDL)为每个生成的语言代码提供类型描述。 //简单来讲就是说thrift 很nb的意思

Base Types
基本类型的选择是因为其简单明了,而不是因其丰富(The base types were selected with the goal of simplicity and clarity rather than abundance),下面来看看:

  • bool: (true/false)
  • byte :8bit有符号整数
  • i16:16bit有符号整数
  • i32:32bit有符号整数
  • i64:64bit有符号整数
  • double:64bit浮点型数
  • string:utf-8编码字符
    注意无符号整数类型,(Note the absence of unsigned integer types.),没有是因为很多编程语言都没有无符号整数类型。

Special Types

  • binary:字节序列
  • N.B. :这是上面string的特性形式( specialized form),是为了用java提供更好的互动性。

Struts
Thrift struts定义一个普通对象,他们基本等价于面向对象程序设计语言的类,也是没有继承,一个struts有一套强类型fields,每个fields都有一个唯一name,fields可能由各种各样的注解 //对应protobuf的message

Containers

  • list:一个由t类型组成的有序可重复
  • set:一个由t类型组成的无序列不可重复
  • map:key-value

Exceptions
跟struts功能差不多,用于异常处理

Services
使用Thrift的类型定义Services,跟定义一个面向对象的程序设计中的接口语义差不多。编译器会生成实现接口全部功能的客户端和服务端。
Services由一组命名函数组成,每个函数都有一个参数列表和一个返回类型
注意void也是一个有效的返回类型,单向修饰符关键字(an oneway modifier keyword)修饰void类型的函数,用于生成那些不用响应的代码。

类型定义 -- typedef
如果不习惯使用thrift定义的基本类型,可以使用typedef进行类型的定义
在文件开头

typedef i32 int
typedef i64 long

在后面就可以使用int和long 分别表示 i32 和i64

示例
Struts

strut Peopel{
 1:string name;
 2:i32 age;
 3:string gender;
}

exception RequestException{
 1:i32 code;
 2:string reason;
}

service HelloWorldService{
 //相当于java接口中定义的函数 -- 若干个方法的集合
 string doAction(1:string name,2:i32 age);
}