Demo 包含以下内容
- thrift Demo
- swift(Facebook 开放基于注解的 thrift) 项目Demo
- swift with Spring MVC
- swift with Spring boot
Apache Thrift 是一个跨语言的RPC框架, 源于Facebook, 目前已经作为开源项目提交给了Apache 软件基金会。Thrift解决了Facebook各系统的大数据量传输通信和内部不同语言环境的跨平台调用。 它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Go,Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。
RPC,全称 Remote Procedure Call(远程过程调用),即调用远程计算机上的服务,就像调用本地服务一样。
Thrift是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务。它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为“大规模跨语言服务开发”而开发的。它通过一个代码生成引擎联合了一个软件栈,来创建不同程度的、无缝的跨平台高效服务,可以使用C#、C++(基于POSIX兼容系统)、Cappuccino、Cocoa、Delphi、Erlang、Go、Haskell、Java、Node.js、OCaml、Perl、PHP、Python、Ruby和Smalltalk。该实现被描述在2007年4月的一篇由Facebook发表的技术论文中,该论文现由Apache掌管。
Thrift的官方网站: http://thrift.apache.org/
作为一个高性能的RPC框架,Thrift的主要特点有:
- 基于二进制的高性能的编解码框架
- 基于NIO的底层通信
- 相对简单的服务调用模型
- 使用IDL支持跨平台调用
Thrift的核心组件有:
- TProtocol 协议和编解码组件
- TTransport 传输组件
- TProcessor 服务调用组件
- TServer,Client 服务器和客户端组件
- IDL 服务描述组件,负责生产跨平台客户端
安装Thrift主要是为了通过IDL文件生成需要的接口和类。
下载地址: https://thrift.apache.org/download
C++ 如果需要支持 C++ 需要安装依赖
Pull in the main thrift dependencies (taken from http://thrift.apache.org/docs/install/ubuntu/ ):
sudo apt install libboost-dev libboost-test-dev libboost-program-options-dev libevent-dev automake libtool flex bison pkg-config g++ libssl-dev
Java 如果需要支持 Java 需要安装 ant
sudo apt-get install ant
Python 如果需要支持 Python 需要安装如下依赖
sudo apt-get install python-all python-all-dev python-all-dbg
安装:
wget http://mirror.fibergrid.in/apache/thrift/0.10.0/thrift-0.10.0.tar.gz
tar -xvf thrift-0.10.0.tar.gz
cd thrift-0.10.0
./configure
make #有点慢
sudo make install
thrift -version
安装时遇到问题:
BUILD FAILED
/home/ev/thrift/thrift-0.10.0/lib/java/build.xml:99: Unable to find a javac compiler;
com.sun.tools.javac.Main is not on the classpath.
Perhaps JAVA_HOME does not point to the JDK.
It is currently set to "/usr/lib/jvm/java-8-openjdk-amd64/jre"
在安装的过程中遇到thrift找不到 JAVA_HOME
的错误,而明明我的 JAVA_HOME
是配置了的,想到当时我安装 Java 时手动安装,手动配置的目录,可能系统找到了 openjdk 的目录,而没有知道我安装的 Oracle JDK,在查询一番之后在官网 找到方法,在 configure 时指定 JAVAC 地址 JAVAC=/usr/local/jdk1.8.0_131/bin/javac
。指定 JAVAC 地址之后依然 install 不行,所以按照这个教程 重新安装了 JDK,然后OK。
Thrift类型系统包括预定义基本类型,用户自定义结构体,容器类型,异常和服务定义
thrift 支持的基本类型
bool:布尔类型(true or value),占一个字节
byte:有符号字节
i16: 16位有符号整型
i32: 32位有符号整型
i64: 64位有符号整型
double:64位浮点数
string:未知编码或者二进制的字符串
注意,thrift不支持无符号整型,因为很多目标语言不存在无符号整型(如java)。
Thrift 允许用户定义常量,复杂类型可用 JSON 表示
const double PI = 3.14;
const map<string,string> MAP = {"hello": "world", "first": "second"}
未加密的字节流
binary: 二进制数据
Thrift提供了3种容器类型:
List<T>:一系列T类型的元素组成的有序表,元素可以重复; 对应 C++ STL vector, Java 的 ArrayList 等等
Set<T>:一系列T类型的元素组成的无序表,元素不可以重复; 对应 STL 的 set, Java 的 HashSet
Map<t1,t2>:key/value对(key的类型是t1且key唯一,value类型是t2); 对应 STL 的 map, Java 的 HashMap 等等
容器中的元素类型可以是除了service意外的任何合法thrift类型(包括结构体和异常)。
Thrift结构体在概念上同C语言结构体类型----一种将相关属性聚集(封装)在一起的方式。在面向对象语言中,thrift结构体被转换成类。在弱类型语言、动态语言中,表现为“结构/结构体”。
异常在语法和功能上类似于结构体,只不过异常使用关键字exception而不是struct关键字声明。但它在语义上不同于结构体----当定义一个RPC服务时,开发者可能需要声明一个远程方法抛出一个异常。
举例:
struct Example {
1:i32 num=1,
2:string name="thrift"
}
exception InvalidOperation {
1:i32 what,
2:string why
}
thrift 提供两个关键字 required
和 optional
,分别用于表示对应字段必填还是可选,比如
struct People {
1: required string name,
2: optional i32 age;
}
表示 name 必填, age 可选。
在一个结构体中,如果field之间的关系是互斥的,即只能有一个field被使用被赋值。在这种情况下,我们可以使用union来声明这个结构体,而不是一堆堆optional的field,语意上也更明确了。例如:
union JavaObjectArg {
1: i32 int_arg;
2: i64 long_arg;
3: string string_arg;
4: bool bool_arg;
5: binary binary_arg;
6: double double_arg;
}
一个服务包含一组方法, 服务的定义方法在语法上等同于面向对象语言中定义接口,
基本语法:
service <name> {
<return type> <method name> (<arguments>) [throws (<exceptions>)]
}
service UserService {
void create(1:i32 id, 2:string name);
string get(1:i32 id);
list<string> getAll();
}
这部分可以在 Demo 中 hello.thrift 中看到
Thrift 的命名空间相当于 Java 中的 package ,主要目的是为了组织代码。 thrift 使用关键字 namespace :
namespace java info.einverne.thrift.demo
namespace java.swift info.einverne.swift.demo
由此生成的代码,包路劲为 info.einverne.thrift.demo
这样。
使用 thrift 命令生成 IDL
thrift -r --gen java src/main/resources/hello.thrift
协议和编解码是一个网络应用程序的核心问题之一,客户端和服务器通过约定的协议来传输消息(数据),通过特定的格式来编解码字节流,并转化成业务消息,提供给上层框架调用。
Thrift 把协议和编解码整合在一起。抽象类TProtocol定义了协议和编解码的顶层接口。TProtocol关联了一个TTransport传输对象,而不是提供一个类似getTransport()的接口。
TProtocol主要做了两个事情:
- 关联TTransport对象
- 定义一系列读写消息的编解码接口,包括两类,一类是复杂数据结构比如readMessageBegin, readMessageEnd, writeMessageBegin, writMessageEnd.还有一类是基本数据结构,比如readI32, writeI32, readString, writeString
所谓协议就是客户端和服务器端约定传输什么数据,如何解析传输的数据。对于一个RPC调用的协议来说,要传输的数据主要有:
调用方
- 方法的名称,包括类的名称和方法的名称
- 方法的参数,包括类型和参数值
- 一些附加的数据,比如附件,超时事件,自定义的控制信息等等
返回方
- 调用的返回码
- 返回值
- 异常信息
TProtocol定义了基本的协议信息,将内存中数据映射成可以传输的机制,包括传输什么数据,如何解析传输的数据的基本方法。
Transport层提供了一个简单的网络读写抽象层。
Server将以上所有特性集成在一起:
- 创建一个transport对象
- 为transport对象创建输入输出protocol
- 基于输入输出protocol创建processor
- 等待连接请求并将之交给processor处理
thrift 定义的 TProtocol 种类
- TBinaryProtocol : 二进制格式
- TCompactProtocol : 压缩格式,高效率、密集二进制编码格式
- TJSONProtocol : JSON格式
- TSimpleJSONProtocol : 提供JSON只写协议, 生成的文件很容易通过脚本语言解析。
- TProtocolDecorator
- TSocket:阻塞式socker;
- TFramedTransport:使用非阻塞方式,以frame为单位进行传输。
- TFileTransport:以文件形式进行传输。
- TMemoryTransport:将内存用于I/O. java实现时内部实际使用了简单的ByteArrayOutputStream。
- TZlibTransport:使用zlib进行压缩, 与其他传输方式联合使用。当前无java实现。
- TNonblockingTransport : 使用非阻塞方式,用于构建异步客户端
- TSimpleServer:单线程服务器端使用标准的阻塞式 I/O,简单的单线程服务模型,常用于测试;
- TThreadPoolServer:多线程服务模型,使用标准的阻塞式IO;
- TNonblockingServer:多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式)
- THsHaServer 所有消息是被调用select()方法的同一个线程处理的
- TThreadedSelectorServer 允许用多个线程来处理网络I/O,它维护了两个线程池,一个用来处理网络I/O,另一个用来进行请求的处理。当网络I/O是瓶颈的时候,TThreadedSelectorServer 比 THsHaServer的表现要好。
thrift文件内容可能会随着时间变化的。如果已经存在的消息类型不再符合设计要求,比如,新的设计要在message格式中添加一个额外字段,但你仍想使用以前的thrift文件产生的处理代码。如果想要达到这个目的,只需:
- 不要修改已存在域的整数编号
- 新添加的域必须是optional的,以便格式兼容。对于一些语言,如果要为optional的字段赋值,需要特殊处理
- 非required域可以删除,前提是它的整数编号不会被其他域使用。对于删除的字段,名字前面可添加“OBSOLETE_”以防止其他字段使用它的整数编号
- thrift文件应该是unix格式的(windows下的换行符与unix不同,可能会导致你的程序编译不过),如果是在window下编写的,可使用dos2unix转化为unix格式。
swift-generator-cli 是 Facebook swift 项目中的一个子项目,该工具用来将 thrift 接口定义转变成 swift 的注解定义
将最新的 Generator 拷贝到 /tmp 目录
mvn org.apache.maven.plugins:maven-dependency-plugin:2.8:get -DremoteRepositories=central::default::http://repo1.maven.apache.org/maven2 -Dartifact=com.facebook.swift:swift-generator-cli:RELEASE:jar:standalone -Ddest=/tmp/
帮助
java -jar /tmp/swift-generator-cli-0.23.1-standalone.jar
Usage: SwiftGenerator [options] Thrift IDL input files
Options:
-default_package
Use this package if there is no package specified in the IDL for java
-generate_beans
Generate thrift types as mutable beans
Default: false
-generate_included_files
Generate code for included IDL files as well as specified IDL files
Default: false
-include_paths
Colon-separated list of paths to search for include files
Default: []
-out
Output directory
Default: /home/mi/Git/thrift-swift-demo/gen-swift
-override_package
Force generation of code in a specific package
-tweak
Enable specific code generation tweaks
Default: []
-use_java_namespace
Use 'java' namespace instead of 'java.swift' namespace
Default: false
定义 thrift 文件
namespace java.swift swift
service BookService {
void ping();
}
生成 swift 定义文件
java -jar /tmp/swift-generator-cli-0.23.1-standalone.jar -out src/main/java src/main/java/swift/book.thrift
该 swift-generator-cli 命令能够实现将 thrift 定义转到 swift 定义文件。
Q: @Override is not allowed when implementing interface method
A: If your project has multiple modules, also check that every module uses language level 6 or above, or use the project's language level (see Project Settings > Modules > xxx > Language level).
You may need to reload your project once it is modified.
Q: thrift 数据传输协议 A:
TBinaryProtocol : 二进制格式.
TCompactProtocol : 压缩格式
TJSONProtocol : JSON格式
TSimpleJSONProtocol : 提供JSON只写协议, 生成的文件很容易通过脚本语言解析
tips:客户端和服务端的协议要一致
Q: NULL 问题 A: thrift 中当调用值返回 null 时 thrift 会抛出 TApplicationException 异常。
} catch (TException e) {
if (e instanceof TApplicationException && ((TApplicationException) e).getType() == TApplicationException.MISSING_RESULT) {
System.out.println("The result of helloNull function is NULL");
}
}
可以 catch 住 TApplicationException ,然后打印日志