什么是thrift
thrift是rpc框架(远程过程调用)中的一种,当然还有其他的rpc框架,这里我们只介绍thrift框架。
rpc框架是一个统称,thrift框架是其中的一种。
(这就好比开发语言是一个统称,golang是其中的一种,python也是其中的一种)
Thrift最初由Facebook研发,目前已经开源到Apache(贡献给apache基金会了)
已广泛应用于业界,主要用于各个服务之间的RPC通信,是一种可扩展,跨语言的服务开发框架。
支持跨语言,常用的语言比如C++, Java, Python, PHP, Ruby, Erlang,Perl,Haskell,
C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml都支持。
比如小明是java开发工程师,他开发了一个服务(其中有很多功能函数/方法),可以供被别人调用。
小红是python开发工程师,这样是不是小红就不能调用小明的服务呢?当然不是。
小红可以通过thrift这个rpc远程过程调用框架,来调用小明的服务,这就是跨语言。
小红自己不用再重复造轮子了,可以去调用小明的功能函数。
Thrift是一个典型的CS(客户端/服务端)结构,客户端和服务端可以使用不同的语言开发(跨语言的)。
既然客户端和服务端可以使用不同的语言开发,那么一定就要有一种`中间语言`来关联客户端和服务端,
这种中间语言就是IDL (Interface Description Language),接口描述语言。
接口描述/定义语言,可以理解为一份服务的说明书。
该说明书中,说明了有哪些函数方法,每个函数方法的入参是啥,是什么类型,输出是啥。
这样调用的人才能清楚知道该如何调用别人的服务。
IDL语言写的文件,都是以.thrift作为后缀的文件。
.thrift文件中的语言就是IDL语言。
用IDL语言编写的文件,就是.thrift文件。
比如上面说的小明和小红的故事,小明用java语言开发了一个服务,部署在一台或多台服务器上,供大家使用。
那么既然这个服务可以供大家使用,那么小明必须要写一个接口文件,让大家知道他的服务中都有哪些函数可以被调用,需要传入什么参数,可以获得什么结果。
所以,小明需要用IDL言语写一份xxx.thrift文件,在该文件中,小明需要详细得说明白:服务名称叫什么,服务中有哪些函数,每个函数的入参是什么,是什么类型的,输出是什么,输出是什么类型的。
最后小红在调用这个服务的时候,她需要拿到这个.thrift文件,然后生成自己的开发语言的代码。
因为小红是python开发,所以她需要通过thrft这个工具(当然需要先安装这个工具)来生成python的代码,命令如下所示:
thrift --gen py xxx.thrift
RPC
rpc是一个概念,是一个统称。并不是一个具体的工具。既然是概念,了解就行了。
RPC, 远程过程调用,直观说法就是A通过网络调用B的过程方法。
简单的说,RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方,并得到返回的结果。
RPC会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯),RPC是一个请求响应模型。
客户端发起请求,服务器返回响应(类似于Http的工作方式),
RPC在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。
什么是thrift接口
Thrift接口是一种基于Thrift框架来定义的接口,就是thrift接口
Thrift 是一个高效、跨语言的 RPC(远程过程调用)框架。
通过 Thrift 接口,可以实现不同语言编写的服务之间的通信和交互。
Thrift 接口的特点包括:
- 跨语言性:支持多种编程语言,方便不同语言的系统进行集成。
- 高效性:具有较高的性能和效率。
- 简洁性:接口定义相对简洁,易于理解和使用。
在使用 Thrift 接口时,需要进行以下步骤:
-
定义接口:使用 Thrift 语法定义服务的方法和参数。
-
生成代码:根据定义的接口生成相应语言的客户端和服务器端代码。
-
实现服务:在服务器端实现接口定义的方法。
-
调用服务:在客户端使用生成的代码调用服务器端的服务。
Thrift 接口常用于构建分布式系统,使得不同语言编写的模块能够方便地进行通信和协作。
thrift的数据类型
byte:有符号字节
i16:16位有符号整数
i32:32位有符号整数
i64:64位有符号整数
double:64位浮点数
string:字符串类型
thrift的容器类型
list: 一系列由T类型的数据组成的有序列表,元素可以重复
set: 一系列由T类型的数据组成的无序集合,元素不可重复
map: 一个字典结构,key为K类型,value为V类型,相当于Java中的HashMap
Thrift工作原理
如何实现多语言之间的通信?
数据传输使用socket(多种语言均支持),数据再以特定的格式(String等)发送,接收方语言进行解析。
定义.thrift的文件,由thrift文件(IDL)生成双方语言的接口、model,在生成的model以及接口中会有解码编码的代码。
Thrift结构体struct
就像C语言一样,Thrift支持struct类型,目的就是将一些数据聚合在一起,方便传输管理。
struct的定义
形式如下: 前面是序号,从1开始,然后是冒号,然后是数据类型,然后是变量名
struct People{
1:string name;
2: i32 age;
3: string gender;
}
Thrift枚举
枚举的定义形式和Java的Enum定义类似
enum Gender{
MALE,
FEMALE
}
Thrift异常
Thrift支持自定义exception,规则与struct一样
exception RequestException{
1:i32 code;
2: string reason:
}
Thrift服务
Thrift定义服务相当于Java中创建Interface一样,创建的service经过代码生成命令之后就会生成客户端和服务端的框架代码。
定义形式如下:
service HelloWordService { //这里服务名和 {之间要有空格
// service中定义的函数,相当于Java interface中定义的方法
string doAction(1:string name,2: i32 age);
}
Thrift类型定义
Thrift支持类似C++一样的typedef定义:
typedef i32 int
typedef i64 long
定义别名
Thrift常量
thrift也支持常量定义,使用const关键字:
const i32 MAX_RETRIES_TIME=10
const string MY_WEBSITE= "https://blog.csdn.net/BADAO_LIUMANG_QIZHI'
命名空间
Thrift的命名空间相当于Java中的package的意思,主要目的是组织代码。thrift使用关键字namespace 定义命名空间:
namespace java com.bdao.thrift
格式是: namespace 语言名 路径
可选和必选
Thrift提供两个关键字required, optional,分别用于表示对应的字段是必填的还是可选的
struct People{
1:required string name;
2:optional i32 age;
}
.thrift文件的示例
IDL语言
namespace c_glib TTest
namespace cpp thrift.test
namespace delphi Thrift.Test
namespace go thrifttest
namespace java thrift.test
namespace js ThriftTest
namespace lua ThriftTest
namespace netstd ThriftTest
namespace perl ThriftTest
namespace php ThriftTest
namespace py ThriftTest
namespace py.twisted ThriftTest
namespace rb Thrift.Test
namespace st ThriftTest
namespace xsd test (uri = 'http://thrift.apache.org/ns/ThriftTest')
// Presence of namespaces and sub-namespaces for which there is
// no generator should compile with warnings only
namespace noexist ThriftTest
namespace cpp.noexist ThriftTest
namespace * thrift.test
/**
* Docstring!
*/
enum Numberz
{
ONE = 1,
TWO,
THREE,
FIVE = 5,
SIX,
EIGHT = 8
}
const Numberz myNumberz = Numberz.ONE;
// the following is expected to fail:
// const Numberz urNumberz = ONE;
typedef i64 UserId
struct Bonk
{
1: string message,
2: i32 type
}
typedef map<string,Bonk> MapType
struct Bools {
1: bool im_true,
2: bool im_false,
}
struct Xtruct
{
1: string string_thing,
4: i8 byte_thing,
9: i32 i32_thing,
11: i64 i64_thing
}
struct Xtruct2
{
1: i8 byte_thing, // used to be byte, hence the name
2: Xtruct struct_thing,
3: i32 i32_thing
}
struct Xtruct3
{
1: string string_thing,
4: i32 changed,
9: i32 i32_thing,
11: i64 i64_thing
}
struct Insanity
{
1: map<Numberz, UserId> userMap,
2: list<Xtruct> xtructs
} (python.immutable= "")
struct CrazyNesting {
1: string string_field,
2: optional set<Insanity> set_field,
// Do not insert line break as test/go/Makefile.am is removing this line with pattern match
3: required list<map<set<i32> (python.immutable = ""), map<i32,set<list<map<Insanity,string>(python.immutable = "")> (python.immutable = "")>>>> list_field,
4: binary binary_field
}
union SomeUnion {
1: map<Numberz, UserId> map_thing,
2: string string_thing,
3: i32 i32_thing,
4: Xtruct3 xtruct_thing,
5: Insanity insanity_thing
}
exception Xception {
1: i32 errorCode,
2: string message
}
exception Xception2 {
1: i32 errorCode,
2: Xtruct struct_thing
}
struct EmptyStruct {}
struct OneField {
1: EmptyStruct field
}
service ThriftTest
{
/**
* Prints "testVoid()" and returns nothing.
*/
void testVoid(),
/**
* Prints 'testString("%s")' with thing as '%s'
* @param string thing - the string to print
* @return string - returns the string 'thing'
*/
string testString(1: string thing),
/**
* Prints 'testBool("%s")' where '%s' with thing as 'true' or 'false'
* @param bool thing - the bool data to print
* @return bool - returns the bool 'thing'
*/
bool testBool(1: bool thing),
/**
* Prints 'testByte("%d")' with thing as '%d'
* The types i8 and byte are synonyms, use of i8 is encouraged, byte still exists for the sake of compatibility.
* @param byte thing - the i8/byte to print
* @return i8 - returns the i8/byte 'thing'
*/
i8 testByte(1: i8 thing),
/**
* Prints 'testI32("%d")' with thing as '%d'
* @param i32 thing - the i32 to print
* @return i32 - returns the i32 'thing'
*/
i32 testI32(1: i32 thing),
/**
* Prints 'testI64("%d")' with thing as '%d'
* @param i64 thing - the i64 to print
* @return i64 - returns the i64 'thing'
*/
i64 testI64(1: i64 thing),
/**
* Prints 'testDouble("%f")' with thing as '%f'
* @param double thing - the double to print
* @return double - returns the double 'thing'
*/
double testDouble(1: double thing),
/**
* Prints 'testBinary("%s")' where '%s' is a hex-formatted string of thing's data
* @param binary thing - the binary data to print
* @return binary - returns the binary 'thing'
*/
binary testBinary(1: binary thing),
/**
* Prints 'testStruct("{%s}")' where thing has been formatted into a string of comma separated values
* @param Xtruct thing - the Xtruct to print
* @return Xtruct - returns the Xtruct 'thing'
*/
Xtruct testStruct(1: Xtruct thing),
/**
* Prints 'testNest("{%s}")' where thing has been formatted into a string of the nested struct
* @param Xtruct2 thing - the Xtruct2 to print
* @return Xtruct2 - returns the Xtruct2 'thing'
*/
Xtruct2 testNest(1: Xtruct2 thing),
/**
* Prints 'testMap("{%s")' where thing has been formatted into a string of 'key => value' pairs
* separated by commas and new lines
* @param map<i32,i32> thing - the map<i32,i32> to print
* @return map<i32,i32> - returns the map<i32,i32> 'thing'
*/
map<i32,i32> testMap(1: map<i32,i32> thing),
/**
* Prints 'testStringMap("{%s}")' where thing has been formatted into a string of 'key => value' pairs
* separated by commas and new lines
* @param map<string,string> thing - the map<string,string> to print
* @return map<string,string> - returns the map<string,string> 'thing'
*/
map<string,string> testStringMap(1: map<string,string> thing),
/**
* Prints 'testSet("{%s}")' where thing has been formatted into a string of values
* separated by commas and new lines
* @param set<i32> thing - the set<i32> to print
* @return set<i32> - returns the set<i32> 'thing'
*/
set<i32> testSet(1: set<i32> thing),
/**
* Prints 'testList("{%s}")' where thing has been formatted into a string of values
* separated by commas and new lines
* @param list<i32> thing - the list<i32> to print
* @return list<i32> - returns the list<i32> 'thing'
*/
list<i32> testList(1: list<i32> thing),
/**
* Prints 'testEnum("%d")' where thing has been formatted into its numeric value
* @param Numberz thing - the Numberz to print
* @return Numberz - returns the Numberz 'thing'
*/
Numberz testEnum(1: Numberz thing),
/**
* Prints 'testTypedef("%d")' with thing as '%d'
* @param UserId thing - the UserId to print
* @return UserId - returns the UserId 'thing'
*/
UserId testTypedef(1: UserId thing),
/**
* Prints 'testMapMap("%d")' with hello as '%d'
* @param i32 hello - the i32 to print
* @return map<i32,map<i32,i32>> - returns a dictionary with these values:
* {-4 => {-4 => -4, -3 => -3, -2 => -2, -1 => -1, }, 4 => {1 => 1, 2 => 2, 3 => 3, 4 => 4, }, }
*/
map<i32,map<i32,i32>> testMapMap(1: i32 hello),
/**
* So you think you've got this all worked out, eh?
*
* Creates a map with these values and prints it out:
* { 1 => { 2 => argument,
* 3 => argument,
* },
* 2 => { 6 => <empty Insanity struct>, },
* }
* @return map<UserId, map<Numberz,Insanity>> - a map with the above values
*/
map<UserId, map<Numberz,Insanity>> testInsanity(1: Insanity argument),
/**
* Prints 'testMulti()'
* @param i8 arg0 -
* @param i32 arg1 -
* @param i64 arg2 -
* @param map<i16, string> arg3 -
* @param Numberz arg4 -
* @param UserId arg5 -
* @return Xtruct - returns an Xtruct with string_thing = "Hello2, byte_thing = arg0, i32_thing = arg1
* and i64_thing = arg2
*/
Xtruct testMulti(1: i8 arg0, 2: i32 arg1, 3: i64 arg2, 4: map<i16, string> arg3, 5: Numberz arg4, 6: UserId arg5),
/**
* Print 'testException(%s)' with arg as '%s'
* @param string arg - a string indication what type of exception to throw
* if arg == "Xception" throw Xception with errorCode = 1001 and message = arg
* else if arg == "TException" throw TException
* else do not throw anything
*/
void testException(1: string arg) throws(1: Xception err1),
/**
* Print 'testMultiException(%s, %s)' with arg0 as '%s' and arg1 as '%s'
* @param string arg - a string indicating what type of exception to throw
* if arg0 == "Xception" throw Xception with errorCode = 1001 and message = "This is an Xception"
* else if arg0 == "Xception2" throw Xception2 with errorCode = 2002 and struct_thing.string_thing = "This is an Xception2"
* else do not throw anything
* @return Xtruct - an Xtruct with string_thing = arg1
*/
Xtruct testMultiException(1: string arg0, 2: string arg1) throws(1: Xception err1, 2: Xception2 err2)
/**
* Print 'testOneway(%d): Sleeping...' with secondsToSleep as '%d'
* sleep 'secondsToSleep'
* Print 'testOneway(%d): done sleeping!' with secondsToSleep as '%d'
* @param i32 secondsToSleep - the number of seconds to sleep
*/
oneway void testOneway(1:i32 secondsToSleep)
}
service SecondService
{
/**
* Prints 'testString("%s")' with thing as '%s'
* @param string thing - the string to print
* @return string - returns the string 'thing'
*/
string secondtestString(1: string thing)
}
struct VersioningTestV1 {
1: i32 begin_in_both,
3: string old_string,
12: i32 end_in_both
}
struct VersioningTestV2 {
1: i32 begin_in_both,
2: i32 newint,
3: i8 newbyte,
4: i16 newshort,
5: i64 newlong,
6: double newdouble
7: Bonk newstruct,
8: list<i32> newlist,
9: set<i32> newset,
10: map<i32, i32> newmap,
11: string newstring,
12: i32 end_in_both
}
struct ListTypeVersioningV1 {
1: list<i32> myints;
2: string hello;
}
struct ListTypeVersioningV2 {
1: list<string> strings;
2: string hello;
}
struct GuessProtocolStruct {
7: map<string,string> map_field,
}
struct LargeDeltas {
1: Bools b1,
10: Bools b10,
100: Bools b100,
500: bool check_true,
1000: Bools b1000,
1500: bool check_false,
2000: VersioningTestV2 vertwo2000,
2500: set<string> a_set2500,
3000: VersioningTestV2 vertwo3000,
4000: list<i32> big_numbers
}
struct NestedListsI32x2 {
1: list<list<i32>> integerlist
}
struct NestedListsI32x3 {
1: list<list<list<i32>>> integerlist
}
struct NestedMixedx2 {
1: list<set<i32>> int_set_list
2: map<i32,set<string>> map_int_strset
3: list<map<i32,set<string>>> map_int_strset_list
}
struct ListBonks {
1: list<Bonk> bonk
}
struct NestedListsBonk {
1: list<list<list<Bonk>>> bonk
}
struct BoolTest {
1: optional bool b = true;
2: optional string s = "true";
}
struct StructA {
1: required string s;
}
struct StructB {
1: optional StructA aa;
2: required StructA ab;
}
struct OptionalSetDefaultTest {
1: optional set<string> with_default = [ "test" ]
}