thrift-入门介绍

1,883 阅读17分钟

thrift简介

Thrift是Facebook贡献的一个开源RPC(远程过程调用)框架, 用来进行可扩展且跨语言的服务器开发。 Thrift允许定义一个服务描述文件IDL描述数据类型和服务接口, 作为输入文件, 编译器生成代码来方便地RPC客户端和服务器通信的无缝跨编程语言。

thrift的使用步骤

  1. 下载thirft
  2. 构建和安装thrift编译器
  3. 编写thrift代码描述文件IDL
  4. 编译IDL文件生成指定语言的客户端和服务端代码
thrift -gen <language> <Thrift filename>

thrift基础概念

image.png

thirft架构

  +-------------------------------------------+
  | Server                                    |
  | (single-threaded, event-driven etc)       |
  +-------------------------------------------+
  | Processor                                 |
  | (compiler generated)                      |
  +-------------------------------------------+
  | Protocol                                  |
  | (JSON, compact etc)                       |
  +-------------------------------------------+
  | Transport                                 |
  | (raw TCP, HTTP etc)                       |

thrift架构由4部分构成:Server、Processor、Protocol、Tansport.

Transport(传输层)

Transport名为传输层, 传输层为从网络读取、写入网络提供了一个简单的抽象, 这使得Thrift能够将底层传输与其他部分如序列化等解耦。

Trasport定义了一些公开的方法:

  • open: 打开一个连接。
  • close: 关闭一个连接。
  • read: 读取网络。
  • write: 写入网络。
  • flush: 刷新连接。

除了以上的接口, Thrift还提供了一个ServerTransport, 用来接收或创建原始传输对象,ServerTransport用来在服务端为传入链接创建新的传输对象。ServerTransport提供以下几个方法:

  • open: 打开服务端连接。
  • listen: 监听客户端连接。
  • accept: 接收客户端连接,并创建新的传输对象。
  • close: 关闭服务端连接。

大多数Thrift可用的语言支持以下几种传输方式:

  • 文件:读取、写入磁盘上的文件
  • http: http协议
  • tcp: tcp协议

Tranport主要有以下几种常用实现:

  • TIOStreamTransport和TSocket: 同步阻塞IO Transport。
  • -: 非阻塞IO Transport。
  • TMemoryBuffer: 使用字节流数组TByteArrayOutputStream作为输出流的封装。
  • TMemoryInputTransport: 使用字节数组byte[]作为输入流的封装。
  • TFileTransport: 基于文件的Transport
  • TServerTransport和TNoBlockingServerTransport: 服务端Transport。
  • TSaslTransport:实现了SASL协议的Transport。
  • TFramedTransport: 封装了TMemoryInputTransport做输入流, 封装了 TByteArrayOutputStream做输出流, 读取或写入时会预先读取或写入4个字节作为消息头来表示消息的长度。
  • TFastFramedTransport: 内存利用率更高的一个内存读写缓冲区, 它使用自动增长的byte[], 而不是每次都New一个byte[], 它同TFramedTransport一样使用4个字节作为消息头表示消息长度, 但是它不是线程安全的。

Protocol(协议层)

Protocol名为协议层, 协议层主要抽象了将内存数据结构映射到传输格式的机制, 它指定了数据类型如何用底层传输来编码、解码自己。因此,协议层负责实现控制编码方案和(反)序列化。Protocol层一般支持:JSON、XML、纯文本、Compact Binary等等。

Thrift协议在设计上是面向流的, 不需要任何明确的框架。比如在开始序列化之前, 无需知道一个字符串或数字的长度。 大多数支持Thrift的语言都支持以下协议:

  • JSON
  • Binary
  • Compact

Protocol的实现接口: TProtocol, 它主要提供以下方法:

  • 写入方法:
  1. writeMessageBegin
  2. writeMessageEnd
  3. writeStructBegin
  4. writeStructEnd
  5. writeFieldBegin
  6. writeFieldEnd
  7. writeFieldStop
  8. writeMapBegin
  9. writeMapEnd
  10. writeSetBegin
  11. writeSetEnd
  12. writeListBegin
  13. writeListEnd
  14. writeBool
  15. writeByte
  16. writeI16
  17. writeI32
  18. writeI64
  19. writeDouble
  20. writeString
  21. writeBinary
  • 读取方法:
  1. readMessageBegin
  2. readMessageEnd
  3. readStructBegin
  4. readStructEnd
  5. readFieldBegin
  6. readFieldEnd
  7. readMapBegin
  8. readMapEnd
  9. readListBegin
  10. readListEnd
  11. readSetBegin
  12. readSetEnd
  13. readBool
  14. readByte
  15. readI16
  16. readI32
  17. readI64
  18. readDouble
  19. readString
  20. readBinary 可以看到Protocol提供了不同数据类型的读取和写入方法,主要是将Transport传输的二进制流读取对应的数据结构或将数据结构写入Transport的二进制流。

Protocol默认实现了几种常用的Protocol协议:

  • TJSONProtocol: JSON格式
  • TBinaryProtocol:二进制格式
  • TCompactProtocol: 压缩格式
  • TSimpleJSONProtocol: 提供的JSON wirte-only协议,生成的文件很方便脚本解析。

Processor(处理层)

Processor名为处理层, 负责从输入流读取数据和写入输出流的能力, 输入和输出流由协议对象表示, 处理器接口非常简单。

interface TProcessor {
    bool process(TProtocol in, TProtocol out) throws TException
}

特定于服务器的处理器实现由编译器生成。 处理器本质上从线路读取数据(使用输入协议), 奖处理委托给处理程序(由用户实现), 并通过线路写入数据(使用输出协议)。

官方默认提供了2个Processor实现:

  • TBaseAsyncProcessor: 异步处理的Processor.
  • TMultiplexedProcessor: 多路处理的Processor.

Server(服务层)

Server名为服务层, 服务层汇集了以上功能:

  • 创建一个Transport
  • 为Transport创建输入输出协议Protocol
  • 基于输入输出Protocol创建Processor
  • 等待传入的连接并将它交给Processor处理

Server提供了以下方法:

  • processorFactory: 设置processor工厂类
  • processor: 设置processor
  • transportFactory: 设置transport工厂类
  • inputTransportFactory: 设置输入流transport工厂类
  • outputTransportFactory: 设置输出流transport工厂类
  • protocolFactory: 设置protocol协议工常类
  • inputProtocolFactory: 设置输入流protocol协议工厂类
  • outputProtocolFactory: 设置输出流protocol协议工厂类
  • serve: 启动服务并接受客户端连接
  • stop: 停止服务
  • isServing: 判断是否服务中

Server的实现接口TServer, 主要提供了以下几种实现:

  • TNonblockingServer: 非阻塞服务
  • THsHaServer: TNonblockingServer的扩展, 支持半同步/半异步, 依赖于TFramedTransport的使用方式。
  • TSaslNonblockingServer: 支持SASL的实现, 异步非阻塞。
  • TSimpleServer: 单线程实现, 仅供测试。
  • TThreadPoolServer: 线程池阻塞服务。
  • TThreadedSelectorServer:线程池非阻塞服务, 一个线程用来接收客户端连接, 一个基于Select IO的线程池处理读取、写入请求。

入门示例

public class Example{
    private static final int PORT = 9300;
    public static void main(String[] args){
      // create Transport 
      TServerSocket serverSocket = new TServerSocket(PORT);
      // create TServer.Args & set Transport
      TServer.Args serverArgs = new TServer.Args(serverSocket);
      
      // set protocol
      serverArgs.protocolFacotory(new TJSONProtocol.Factory());
      
      // set processor
      TProcessor processor = new UserService.Processor<UserService.Iface>(new UserServiceImpl());
      serverArgs.processor(processor);
      
      // create TServer and begin serve
      TServer server = new TSimpleServer(serverArgs);
      server.serve();
    }
}

thirft数据类型

Thrift的数据类型包括基本类型(BaseType)、特殊类型(binary)、结构体(Struct)、容器类型(list、map、set)、异常(Exception)和服务(Service)、枚举(Enum) 等。

基本类型(BaseType)

Thrift支持大多数语言里卖弄支持的一些常用基本类型:

类型长度(bit)描述对应Java中的类型
bool1布尔类型:true/falseboolean
byte88bit有符号整型byte
i161616bit有符号整型short
i323232bit有符号整型int
i646464bit有符号整型long
double6464bit浮点数double
stringUTF-8编码的文本字符串string
注意: Thrift不支持无符号整型, 因为大多数语言不支持无符号整型, 如Java

Thrift IDL中基础类型的定义方式:

const flag: bool = true
const b: byte = 'c'
const i1: i16 = 1
const i2: i32 = 10024
const i3: i64 = 20000000000000000000000000
const d1: double = 1.5
const d2 = 2e-5   // 可以使用科学计数法表示
const s: string = "Hello World"

特殊类型(Special Type)

  • binary: 未编码的字节流, binary对应Java中的ByteBuffer。
  • union: union是一种特殊的结构体, 定义方式和struct差不多,表示结构体中至少有一个字段有值即可, Union中每个字段的FieldReq都是optional。 举例比如注册场景, 手机号和邮箱提供一个即可。

Thrift中特殊类型的定义方式:

struct UploadRequest{
   1: required string fileName;
   2: required binary fileBytes; // binary是二进制数组
}

// 定义一个union类型
union LoginRequest{
  1: string mobile;
  2: string email;
}

容器类型(container)

Thrift容器是强类型容器, 映射到主流编程语言中的大多数容器类型, Thrift提供了3种容器类型:

  • list: 有序列表, 可以映射为STL向量、Java ArrayList、脚本语言中的数组等。
  • set: 无序列表,元素不可以重复。 可以映射为STL集合、Java HashSet、Python中的集合等。
  • map<type1, type2>: key/value对, key必须是唯一,转换为STL映射、Java HashMap、Python/Ruby字典等。 key必须是基础类型。

容器元素可以是任何合法的Thrift元素, 但是map的键必须是基础类型, 以便于兼容各种语言和协议。

IDL中容器类型的定义方式:

// 类似js中的list
const list<string> provinces = ["北京", "上海", "重庆"];
// 类似js中的map
const map<string, string> provinceCodeMap = {"10": "北京", "61": "陕西"};

const set<i16> sexs = [0, 1];

结构体(Struct)

Thrift结构体用来定义一个对象,在概念上同C++中的Struct, 但是不支持继承机制。Struct封装了一系列的强类型的域(字段), 每个域都有唯一的整数标识符、类型、名称和缺省的可选参数。

IDL中定义结构体的方式为:

struct User{
  1: required i64 id;
  2: required string name;
  3: optional i16 age;
  4: optional byte sex;
  5: optional string address;
}

struct Student{
  1: required i64 studentId;
  2: required string schoolNumber;
  3: required string studentNumber;
  4: required User userInfo;
}

异常(Exception)

Thrift中异常(Exception)在功能上等同于结构体(Struct),只不过异常使用关键字exception声明而不是struct声明, 异常用于和各种编程语言中的异常绑定, 以便获取并抛出异常。异常中每个字段的名称必须唯一。

IDL中定义异常(Exception)的方式如下:

exception MyException{
 1: i16 code;
 2: string message;
}

异常(Exception)一般配合throws关键字来使用:

service UserService{
    User get(1: required string name) throws(1: MyException error)
}

服务(Service)

Thrift中服务(Service) 等同于编程语言中的接口或抽象类, 一个服务定义包含了一系列的命名函数, 每个函数包含一系列的参数和一个返回类型。服务(Service)会封装为一个Thrift Server堆外暴露, Thrift编译器根据Service的定义生成实现接口的服务端接口代码和客户端代码。

Thrift中除过所有其他已定义的Thrift类型外, void也是一种合法的返回类型, 同Java中的void。 此外可以用oneway关键字修饰Service中的方法, 这将生成不等待响应的代码。

IDL中服务(Service)的定义如下:

service UserService{
   // 返回值、参数列表、异常列表
   User get(1: requried i64 id) throws (1: MyException e);
   // 无返回值
   void ping();
   // oneway方法的返回值必须是void, oneway不等于异步调用返回结果, 而是异步调用不等待结果。
   oneway void delete(1: required i64 id);
}

注意事项:

  • 方法可以用逗号,或分号;结尾。
  • 参数可以是基本类型或结构体, 入参是只读的, 方法内部对入参的修改不影响入参(值传递)。
  • 返回值可以是void
  • service支持继承, 可以通过extend继承另外一个service。

枚举(Enum)

Thrift中定义枚举的方式和C++中定义枚举的方式一样, 枚举含义也一样, 枚举如果没有设置值,则默认从0开始, 如果要指定,则必须是一个整数, 如果后续没有指定, 则从前一个开始递增, 如下:

enum Status{
   SUCCESS,       // 0
   INVALID_PARAM,   // 1
   ERROR     // 2
}

enum SEX{
   SUNDAY =  0,    
   MONDAY,            // 1
   SATURDAY           // 2
}

thrift基本语法

字面量

thrift中用单引号或双引号引起来的字符串为字面量。

Literal         ::=  ('"' [^"]* '"') | ("'" [^']* "'")

标识符

thrift合法的标识符仅支持数字、字母、下划线-、小数点, 只能用字母或下划线开头。

Identifier      ::=  ( Letter | '_' ) ( Letter | Digit | '.' | '_' )*
STIdentifier    ::=  ( Letter | '_' ) ( Letter | Digit | '.' | '_' | '-' )*

列表分隔符

thrift中列表分隔符的含义和Java中定义, 用来分隔代码,可以是逗号,或分号;。

ListSeparator   ::=  ',' | ';'

字母和数字

thrift中字母和数字的定义如下:

Letter          ::=  ['A'-'Z'] | ['a'-'z']  // 字母必须是a-z和A-Z
Digit           ::=  ['0'-'9']  // 数字必须是0-9

定义常量

Thrift中通过const关键字定义常量, 和C++一致, 定义的方式为:

Const           ::=  'const' FieldType Identifier '=' ConstValue ListSeparator?

比如:

const bool SUCCESS = true,
const i16 HTTP_PORT = 80;
const string VERSION = '0.0.1',
const double PI = 3.14159,

const list<string> CITY_CODES = ['100000', '120000', '610000']

定义类型

thrift中通过typedef关键字来定义类型别名, 同Golang中的type关键字。使用方式如下:

 Typedef         ::=  'typedef' DefinitionType Identifier

举例:

typedef i64 MyInteger,

const MyInteger THREAD_POOL_SIZE = 256;

注释

Thrift支持shell注释风格, C++中单行或多行的注释风格:

# this is a comment

// this is another single line comment

/*
* C++/Java Style multiple line comment
*/

thrift IDL(接口描述文件)

IDL是thrift中的接口描述文件, IDL文件被Thrift编译器编译成指定语言的代码, 支持在IDL定义结构和服务。

IDL由一个或多个Header和Definition组成。

 Document        ::=  Header* Definition*
Header

Header用来定义Thrift IDL的声明, 可以是Thrift Include、 Cpp Include、Namespace(命名空间)。

  • Thrift Include: 引入另外一个Thrift文件,便于组织文件结构,通过文件名作为前缀的方式访问被引入的对象。
  • Cpp Include: 引入C++文件。
  • Namespace: 命名空间描述, 用于声明代码的空间, 同Java的package、Golang的module作用一致。其次命名空间还声明了使用哪种目标语言, *代表所有适用于所有目标语言。

使用方式如下:

  Include         ::=  'include' Literal
  CppInclude      ::=  'cpp_include' Literal
  Namespace       ::=  ( 'namespace' ( NamespaceScope Identifier ) )
  NamespaceScope  ::=  '*' | 'c_glib' | 'cpp' | 'delphi' | 'haxe' | 'go' | 'java' | 'js' | 'lua' | 'netstd' | 'perl' | 'php' | 'py' | 'py.twisted' | 'rb' | 'st' | 'xsd'

示例:

include 'another.thrift'
cpp_include 'annotation.h'
namespace java org.good.thrift.service;

service AnotherService{
    list<another.Tweet> findRelationShip(1: required another.Tweet tweet);
}

Definition

Definition声明Thrift文件中的主体结构, 如结构体、枚举、服务、异常等等, Definition的定义方式如下:

Definition      ::=  Const | Typedef | Enum | Senum | Struct | Union | Exception | Service

它由以下组成, 可以包含一个或多个以下内容:

  • Const 常量定义
  • Typedef 类型定义
  • Enum 枚举定义
  • Struct 结构体定义
  • Union 组合体定义
  • Exception 异常定义
  • Service 服务定义

Field定义

Field和编程语言中的字段、入参含义一致, 在Thrift中可以组成Struct、Union、Exception以及Service中Function的入参等等, 它的定义如下:

Field           ::=  FieldID? FieldReq? FieldType Identifier ('=' ConstValue)? XsdFieldOptions ListSeparator?
  • FieldId: 表示唯一整数标识符, 默认从1开始。
  • FieldReq: 表示字段是否需要, 可以是required或optional, 如果不提供则表示默认的行为。
  • FieldType: 表示数据类型, 可以是任意合法的Thrift类型

示例:

struct Location{
   1: required double lon,
   2: required double lat,
   3: optional double z
}

Function定义

Function和编程语言中的方法或函数含义一致, 包含返回类型、方法名称、方法入参、可选的异常, Thrift中Function的定义如下:

Function        ::=  'oneway'? FunctionType Identifier '(' Field* ')' Throws? ListSeparator?
FunctionType    ::=  FieldType | 'void'
Throws          ::=  'throws' '(' Field* ')'
  • oneway是一种调用方式, 代表调用完不用等待返回就结束,它的返回类型必须是void, 但是它和普通调用方式的void返回含义不一样,void类型的返回还可以返回异常。
  • 返回类型必须是Thrift中合法的数据类型或void。
  • throws表示抛出异常, 和Java中的含义一致。

示例:

service UserService(){
    void sayHello(1: required User user);
    oneway void ping();
    User getUser(1: required string name) throws(1: MyException e);
}

thrift 代码生成

编写thrift IDL文件

namespace java org.good.example.thrift.service

enum Sex{
    MALE,
    FEMALE
}

enum ErrorCode{
    SUCCESS = 0,
    INAVLID_USER = -1,
    INAVLID_PARAM = -10,
    NOT_EXISTS = -100,
    ERROR = -1000
}

struct Location{
    1: required double lon;
    2: required double lat;
}

struct Address{
    1: required string country;
    2: required string province;
    3: required string city;
    4: required string county;
    5: optional Location location;
    6: optional string detail;
}

struct User{
    1: required i64 id;
    2: required string name;
    3: optional Sex sex;
    4: optional i16 age;
    5: optional Address address;
}

exception UserException{
    1: required i64 id,
    2: required ErrorCode errorCode,
    3: optional string message,
}

service UserService{
    User getUser(1: required i64 id) throws(1: UserException e);
    void addUser(1: required User user) throws(1: UserException e);

    bool setUser(1: required i64 id, 2: required User anotherUser) throws(1: UserException e);

    oneway void deleteUser(1: required i64 id);
}

编译指定语言代码

thrift提供了thrift可执行文件来编译IDL生成各种语言的代码, 使用方式为:

thrift -gen <language> -out <outDir> <fileName>

thrift命令有几个重要的参数:

  • -gen <language>: 编译输入的thrift文件并生成指定语言的代码。
  • -o <outdir>: 编译结果的输出文件夹, 默认会生成一个名为gen-<language>文件夹。
  • -out <outdir>: 编译结果的输出文件夹, 不会生成gen-<language>文件夹。
  • -v[erbose]: 输出编译详情。
  • -r[ecurse]: 同时编译include的文件。
  • -I <dir>: 在dir中寻找指定要编译的thrift文件所引用的其他thrift文件。

示例:

thrift -gen java -out ../main/java  user/user.thrift

// 输出
Scanning E:/eclipseProject/syslog/skyeye/good/good-java/good-java-thrift/src/thrift/user/user.thrift for includes
Parsing E:/eclipseProject/syslog/skyeye/good/good-java/good-java-thrift/src/thrift/user/user.thrift for types
Program: E:/eclipseProject/syslog/skyeye/good/good-java/good-java-thrift/src/thrift/user/user.thrift
Generating "java"

thirft代码示例

继续以上一章节编译的代码为例, 学习如何使用thrift生成代码编写客户端和服务端代码。

接口定义

package org.good.example.thrift.service;

@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.16.0)", date = "2022-05-02")
public class UserService {

  public interface Iface {
    // 根据用户id获取用户
    public User getUser(long id) throws UserException, org.apache.thrift.TException;
    // 添加用户
    public void addUser(User user) throws UserException, org.apache.thrift.TException;
    // 更新用户信息
    public boolean setUser(long id, User anotherUser) throws UserException, org.apache.thrift.TException;
    // 删除用户
    public void deleteUser(long id) throws org.apache.thrift.TException;

  }
  
}  

接口实现

package com.good.example.thrift.service.user;

import org.apache.thrift.TException;
import org.good.example.thrift.service.Address;
import org.good.example.thrift.service.User;
import org.good.example.thrift.service.UserException;
import org.good.example.thrift.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserServiceImpl implements UserService.Iface {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
    
    @Override
    public User getUser(long id) throws UserException, TException {
        User user = new User();
        user.setId(id);
        user.setName("test");
        Address address = new Address();
        address.setCountry("中国");
        address.setProvince("北京");
        address.setCity("北京");
        address.setCounty("未知");
        user.setAddress(address);
        LOGGER.info("getUser id={}, user={}", id, user);
        return user;
    }

    @Override
    public void addUser(User user) throws UserException, TException {
        LOGGER.info("addUser id={}, user={}", user.getId(), user);
    }

    @Override
    public boolean setUser(long id, User anotherUser) throws UserException, TException {
        LOGGER.info("setUser id={}, user={}", id, anotherUser);
        return true;
    }

    @Override
    public void deleteUser(long id) throws TException {
        LOGGER.info("deleteUser id={}", id);
    }
}

服务端Server实现

package com.good.example.thrift.server;

import com.good.example.thrift.service.user.UserServiceImpl;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TJSONProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
import org.apache.thrift.transport.layered.TFramedTransport;
import org.good.example.thrift.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

public class SimpleServer {
    private static final int SERVER_PORT = 9001;
    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleServer.class);
    public static void main(String[] args) throws IOException, TTransportException {
        // create transport
        TServerSocket serverSocket = new TServerSocket(SERVER_PORT);
        TServer.Args serverArgs = new TServer.Args(serverSocket);
        // set protocol
        serverArgs.protocolFactory(TBinaryProtocol::new);
        //set processor
        serverArgs.processor(new UserService.Processor<UserService.Iface>(new UserServiceImpl()));
        serverArgs.transportFactory(new TFramedTransport.Factory());

        // run server
        TServer server = new TSimpleServer(serverArgs);
        LOGGER.info("run server at {}...", SERVER_PORT);
        server.serve();
    }
}

客户端Client实现

public class UserClient {
    private static final Logger LOGGER = LoggerFactory.getLogger(UserClient.class);
    public static void main(String[] args) throws TException {
        try {
            TTransport transport = new TSocket("0.0.0.0", 9023);
            TFramedTransport tFramedTransport = new TFramedTransport(transport);
            TProtocol tProtocol = new TBinaryProtocol(tFramedTransport);
            transport.open();

            UserService.Client userService = new UserService.Client(tProtocol);
            User user = userService.getUser(1);
            LOGGER.info(user.toString());

            User newUser = new User();
            newUser.setId(2);
            newUser.setName("test2");
            userService.addUser(newUser);

            userService.setUser(user.getId(), newUser);
            userService.deleteUser(user.getId());
        }catch (Exception e){
            LOGGER.error(e.getMessage(), e);
        }

    }
}

thrift总结

thrift支持的特性

  • 命名空间: 每个thrift文件都有命名空间,允许在多个thrift文件中使用相同的命名空间。
  • 语言命名空间: 每个Thrift文件可以为不同语言设置不同的命名空间。
  • 基本类型: Thrift支持bool、byte、i16、i32、i64、double、string等基础类型。
  • 枚举和常量: thirft支持类C++的枚举和常量。
  • struct: 支持类C++的结构体, 封装数据结构。
  • 稀疏结构:未设置的的可选字段和为空的引用字段不会通过传输层传输。
  • struct evolution: 通过字段的整数标识符, 可以在不影响存量客户端的情况下新增和删除字段。
  • 容器类型: 支持list、set、map3种常用容器。
  • service: 服务是一组功能的组合。
  • 服务继承: thrift支持服务继承。
  • 异步调用: 异步调用是可以调用但不需要返回结果的函数,不会阻塞客户端, 服务端会并行、乱序执行同一客户端的异步调用。
  • 异常: thrift支持异常处理。
  • 循环调用: thrift在0.9.2版本开始支持循环调用。

thrift不支持的特性

  • 继承: thrift不支持继承, 改用组合来实现继承。
  • 多态性:thrift不支持继承, 所以也不支持多态。
  • 重载: 服务中的所有方法必须唯一命名。
  • 异构容器: thrift容器时强类型, 容器中的元素必须类型一致。
  • 空返回: 不能直接从函数返回。改用包装结构或标记值。

thrift java

thrift maven配置

POM 依赖

<dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.16.0</version>
</dependency>

POM 插件

org.apache.thrift.tools

  • POM依赖
<groupId>org.apache.thrift.tools</groupId>
<artifactId>maven-thrift-plugin</artifactId>
<version>0.1.11</version>
  • 配置方式
<plugin>
    <groupId>org.apache.thrift.tools</groupId>
    <artifactId>maven-thrift-plugin</artifactId>
    <version>0.1.11</version>
    <configuration>
        <thriftSourceRoot>${basedir}/src/thrift</thriftSourceRoot>
        <outputDirectory>${basedir}/src/main/java/thrift</outputDirectory>
        <generator>java</generator>
    </configuration>
    <executions>
        <execution>
            <id>thrift-source</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
        <execution>
            <id>thrift-test-source</id>
            <phase>generate-test-sources</phase>
            <goals>
                <goal>testCompile</goal>
            </goals>
        </execution>
    </executions>
</plugin>
  • 关键参数
  • thriftSourceRoot: thrift文件的位置, 默认${basedir}/src/main/thrift。
  • outputDirectory: thrift文件编译代码输出的位置, 默认是${project.build.outputDirectory}, 既target文件目录下, 如果需要设置到/src/main/java下, 需要选择一个新的目录, 否则会把已有的内容覆盖掉, 比如选择src/main/java, 编译时会把src/main/java中的内容全部覆盖。
  • generator: thrift编译器, 既 thrift -gen <language> 中的language, 默认java:hashcode, 会报错, 需要设置为java。
  • thriftExecutable: 本地的thrift可执行文件, 默认thrift, 会在$PATH种查找。
  • includes和excludes: 包含和排除的thrift文件。
  • 特点
  1. 依赖于本地的thrift可执行文件。

com.facebook.mojo

  • POM依赖
<groupId>com.facebook.mojo</groupId>
<artifactId>swift-maven-plugin</artifactId>
<version>0.23.1</version>
  • 配置方式
<plugin>
    <groupId>com.facebook.mojo</groupId>
    <artifactId>swift-maven-plugin</artifactId>
    <version>0.23.1</version>
    <executions>
        <execution>
            <id>swift-thrift-source</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <idlFiles>
            <directory>${basedir}/src/swift</directory>
            <includes>**/*.thrift</includes>
        </idlFiles>
        <outputFolder>${project.basedir}/src/main/swift</outputFolder>
    </configuration>
</plugin>
  • 关键参数
  • idlFiles: thrift文件的存放位置,值是org.apache.maven.model.FileSet, 包含几个关键属性:directory(IDL文件位置,可以使用通配符)、includesexcludes
  • outputFolder: thrift编译结果的输出位置。
  • generateIncludedCode: 是否编译包含的thrift文件, 默认false。
  • 特点

1.不依赖本地的thrift可执行文件。 2.命名空间(Namespace)必须为java.swift。