thrift简介
Thrift是Facebook贡献的一个开源RPC(远程过程调用)框架, 用来进行可扩展且跨语言的服务器开发。 Thrift允许定义一个服务描述文件IDL描述数据类型和服务接口, 作为输入文件, 编译器生成代码来方便地RPC客户端和服务器通信的无缝跨编程语言。
thrift的使用步骤
- 下载thirft
- 构建和安装thrift编译器
- 编写thrift代码描述文件IDL
- 编译IDL文件生成指定语言的客户端和服务端代码
thrift -gen <language> <Thrift filename>
thrift基础概念
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, 它主要提供以下方法:
- 写入方法:
- writeMessageBegin
- writeMessageEnd
- writeStructBegin
- writeStructEnd
- writeFieldBegin
- writeFieldEnd
- writeFieldStop
- writeMapBegin
- writeMapEnd
- writeSetBegin
- writeSetEnd
- writeListBegin
- writeListEnd
- writeBool
- writeByte
- writeI16
- writeI32
- writeI64
- writeDouble
- writeString
- writeBinary
- 读取方法:
- readMessageBegin
- readMessageEnd
- readStructBegin
- readStructEnd
- readFieldBegin
- readFieldEnd
- readMapBegin
- readMapEnd
- readListBegin
- readListEnd
- readSetBegin
- readSetEnd
- readBool
- readByte
- readI16
- readI32
- readI64
- readDouble
- readString
- 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中的类型 |
|---|---|---|---|
| bool | 1 | 布尔类型:true/false | boolean |
| byte | 8 | 8bit有符号整型 | byte |
| i16 | 16 | 16bit有符号整型 | short |
| i32 | 32 | 32bit有符号整型 | int |
| i64 | 64 | 64bit有符号整型 | long |
| double | 64 | 64bit浮点数 | double |
| string | UTF-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文件。
- 特点
- 依赖于本地的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文件位置,可以使用通配符)、includes、excludes。
- outputFolder: thrift编译结果的输出位置。
- generateIncludedCode: 是否编译包含的thrift文件, 默认false。
- 特点
1.不依赖本地的thrift可执行文件。 2.命名空间(Namespace)必须为java.swift。