thrift 的原理和使用

thrift 的原理和使用

Thrift 架构 

Thrift是一个跨语言的服务部署框架,最初由Facebook于2007年开发,2008年进入Apache开源项目。Thrift经过IDL(Interface Definition Language,接口定义语言)来定义RPC(Remote Procedure Call,远程过程调用)的接口和数据类型,而后经过thrift编译器生成不一样语言的代码(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml),并由生成的代码负责RPC协议层和传输层的实现。html

PS:CentOS下的Thrift的安装流程能够参考这里java

 

Thrift架构python

 

图中,TProtocol(协议层),定义数据传输格式,例如:shell

  • TBinaryProtocol:二进制格式;
  • TCompactProtocol:压缩格式;
  • TJSONProtocol:JSON格式;
  • TSimpleJSONProtocol:提供JSON只写协议, 生成的文件很容易经过脚本语言解析;
  • TDebugProtocol:使用易懂的可读的文本格式,以便于debug

TTransport(传输层),定义数据传输方式,能够为TCP/IP传输,内存共享或者文件共享等)被用做运行时库。apache

  • TSocket:阻塞式socker;
  • TFramedTransport:以frame为单位进行传输,非阻塞式服务中使用;
  • TFileTransport:以文件形式进行传输;
  • TMemoryTransport:将内存用于I/O,java实现时内部实际使用了简单的ByteArrayOutputStream;
  • TZlibTransport:使用zlib进行压缩, 与其余传输方式联合使用,当前无java实现;

 

Thrift支持的服务模型centos

  • TSimpleServer:简单的单线程服务模型,经常使用于测试;
  • TThreadPoolServer:多线程服务模型,使用标准的阻塞式IO;
  • TNonblockingServer:多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式);

 

Thrift其实是实现了C/S模式,经过代码生成工具将thrift文生成服务器端和客户端代码(能够为不一样语言),从而实现服务端和客户端跨语言的支持。用户在Thirft文件中声明本身的服务,这些服务通过编译后会生成相应语言的代码文件,而后客户端调用服务,服务器端提服务即可以了。数组

 

通常将服务放到一个.thrift文件中,服务的编写语法与C语言语法基本一致,在.thrift文件中有主要有如下几个内容:变量声明(variable)、数据声明(struct)和服务接口声明(service, 能够继承其余接口)。服务器

下面分析Thrift的tutorial中带的例子tutorial.thrift:网络

复制代码
// 包含头文件
include “shared.thrift”        

// 指定目标语言
namespace cpp tutorial            

// 定义变量
const i32 INT32CONSTANT = 9853        

// 定义结构体
struct Work {
  1: i32 num1 = 0,
  2: i32 num2,
  3: Operation op,
  4: optional string comment,
}

// 定义服务
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.
    */
   oneway void zip()

}
复制代码

 

 

编译thrift文件,生成C++代码:数据结构

./thrift --gen cpp tutorial.thrift   #结果代码存放在gen-cpp目录下

 

若是是要生成java代码:

./thrift --gen java tutorial.thrift  #结果代码存放在gen-java目录下

 

client端和sever端代码要调用编译.thrift生成的中间文件。
下面分析cpp文件下面的CppClient.cpp和CppServer.cpp代码

 

 

 

在client端,用户自定义CalculatorClient类型的对象(用户在.thrift文件中声明的服务名称是Calculator, 则生成的中间代码中的主类为CalculatorClient), 该对象中封装了各类服务,能够直接调用(如client.ping()), 而后thrift会经过封装的rpc调用server端同名的函数。
在server端,须要实如今.thrift文件中声明的服务中的全部功能,以便处理client发过来的请求。

 


 

Thrift语法

Thrift文件支持shell命令,所以thrift是可执行的。

Thrfit支持shell注释风格(#),也支持C/C++语言中单行(//)或者多行(/* */)注释风格

 

数据类型

一、基本类型

  • bool,布尔型,1个字节;
  • byte,有符号单字节;
  • i16,有符号16位整型;
  • i32,有符号32位整型;
  • i64,有符号64位整型;
  • double,64位浮点数;
  • string,字符串;
  • binary,字节数组;

注意:thrift不支持无符号整型。

 

二、容器

  • map<t1,t2>,字典;
  • list<t1>,列表;
  • set<t1>,集合;

注意:容器中的元素类型能够是除了service 之外的任何合法thrift类型(包括结构体和异常)。

 

三、结构体 struct

Thrift结构体在概念上同C语言结构体类型—-一种将相关属性汇集(封装)在一块儿的方式;

在面向对象语言中,thrift结构体被转换成类。

struct Work {
  1: i32 num1 = 0,
  2: i32 num2,
  3: Operation op, 
  4: optional string comment,
}

结构体中,每一个字段包含一个整数ID,数据类型、字段名,和一个可选的默认值。

字段还能够声明为"optional",当该字段没有设置的时候,不会被序列化输出;

规范的struct定义中的每一个域均会使用required或者optional关键字进行标识。若是required标识的域没有赋值,thrift将给予提示。若是optional标识的域没有赋值,该域将不会被序列化传输。若是某个optional标识域有缺省值而用户没有从新赋值,则该域的值一直为缺省值。

 

四、异常 exception

异常在语法和功能上相似于结构体,只不过异常使用关键字exception而不是struct关键字声明。但它在语义上不一样于结构体,当定义一个RPC服务时,开发者可能须要声明一个远程方法抛出一个异常。

exception InvalidOperation {
  1: i32 what,
  2: string why 
}

 

五、服务 service 

在流行的序列化/反序列化框架(如protocol buffer)中,Thrift是少有的提供多语言间RPC服务的框架。

Thrift编译器会根据选择的目标语言为server产生服务接口代码,为client产生桩代码。

复制代码
//“Twitter”与“{”之间须要有空格!!!
service Twitter {
 
// 方法定义方式相似于C语言中的方式,它有一个返回值,一系列参数和可选的异常
 
// 列表. 注意,参数列表和异常列表定义方式与结构体中域定义方式一致.
 
void ping(),                       // 函数定义可使用逗号或者分号标识结束
 
bool postTweet(1:Tweet tweet);    // 参数能够是基本类型或者结构体,参数是只读的(const),不能够做为返回值!!!
 
TweetSearchResult searchTweets(1:string query); // 返回值能够是基本类型或者结构体
 
// ”oneway”标识符表示client发出请求后没必要等待回复(非阻塞)直接进行下面的操做,
 
// ”oneway”方法的返回值必须是void
 
oneway void zip()               // 返回值能够是void
 
}
复制代码

service中的函数,其参数列表的定义方式与struct彻底同样;

service支持继承,一个service可以使用extends关键字继承另外一个service,struct不支持继承;

 

六、枚举类型 enum

复制代码
enum TweetType {
 
TWEET,         // 编译器默认从1开始赋值
RETWEET = 2,  // 能够赋予某个常量某个整数
DM = 0xa,     //容许常量是十六进制整数
REPLY         // 末尾没有逗号
}        

struct Tweet { 1: required i32 userId; 2: required string userName; 3: required string text; 4: optional Location loc; 5: optional TweetType tweetType = TweetType.TWEET // 给常量赋缺省值时,使用常量的全称 16: optional string language = "english" }
复制代码

注意:枚举常量必须是32位的正整数

 

七、常量 const

Thrift容许用户定义常量,复杂的类型和结构体可以使用JSON形式表示。

const i32 INT_CONST = 1234;    // 分号是可选的
const map<string,string> MAP_CONST = {"hello": "world", "goodnight": "moon"}

 

PS:跟C语言相似,Thrift也支持typedef语句,例如:

typedef i32 MyInteger 

 

命名空间

Thrift中的命名空间同C++中的namespace相似,它们均提供了一种组织(隔离)代码的方式。由于每种语言均有本身的命名空间定义方式(如python中有module),thrift容许开发者针对特定语言定义namespace:

namespace cpp com.example.project 
namespace java com.example.project 

 


 

产生代码

下面介绍Thrift产生各类目标语言代码的方式,

 

Thrift的网络栈以下所示:

 

Transport层提供了一个简单的网络读写抽象层。这使得thrift底层的transport从系统其它部分(如:序列化/反序列化)解耦。

如下是一些Transport接口提供的方法:

复制代码
open
close
read
write
listen
accept
flush
复制代码

 

Protocol抽象层定义了一种将内存中数据结构映射成可传输格式的机制。换句话说,Protocol定义了datatype怎样使用底层的Transport对本身进行编解码。所以,Protocol的实现要给出编码机制并负责对数据进行序列化。

Protocol接口的定义以下:

复制代码
writeMessageBegin(name, type, seq)
writeMessageEnd()
writeStructBegin(name)
writeStructEnd()
writeFieldBegin(name, type, id)
writeFieldEnd()
writeFieldStop()
writeMapBegin(ktype, vtype, size)
writeMapEnd()
writeListBegin(etype, size)
writeListEnd()
writeSetBegin(etype, size)
writeSetEnd()
writeBool(bool)
writeByte(byte)
writeI16(i16)
writeI32(i32)
writeI64(i64)
writeDouble(double)
writeString(string)
name, type, seq = readMessageBegin()
readMessageEnd()
name = readStructBegin()
readStructEnd()
name, type, id = readFieldBegin()
readFieldEnd()
k, v, size = readMapBegin()
readMapEnd()
etype, size = readListBegin()
readListEnd()
etype, size = readSetBegin()
readSetEnd()
bool = readBool()
byte = readByte()
i16 = readI16()
i32 = readI32()
i64 = readI64()
double = readDouble()
string = readString()
复制代码

 

Processor封装了从输入数据流中读数据和向数据数据流中写数据的操做。读写数据流用Protocol对象表示。Processor的结构体很是简单:

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

与服务相关的processor实现由编译器产生。Processor主要工做流程以下:从链接中读取数据(使用输入protocol),将处理受权给handler(由用户实现),最后将结果写到链接上(使用输出protocol)。

 

Server将以上全部特性集成在一块儿:
(1) 建立一个transport对象
(2) 为transport对象建立输入输出protocol
(3) 基于输入输出protocol建立processor
(4) 等待链接请求并将之交给processor处理

 

 

 

 原文连接:https://www.cnblogs.com/chenny7/p/4224720.html

 

 

 

参考文档:

http://dongxicheng.org/search-engine/thrift-framework-intro/

http://dongxicheng.org/search-engine/thrift-guide/

http://dongxicheng.org/search-engine/thrift-internals/

http://dongxicheng.org/search-engine/thrift-bidirectional-async-rpc/

thrift 的原理和使用

 

Thrift 架构 

Thrift是一个跨语言的服务部署框架,最初由Facebook于2007年开发,2008年进入Apache开源项目。Thrift经过IDL(Interface Definition Language,接口定义语言)来定义RPC(Remote Procedure Call,远程过程调用)的接口和数据类型,而后经过thrift编译器生成不一样语言的代码(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml),并由生成的代码负责RPC协议层和传输层的实现。

PS:CentOS下的Thrift的安装流程能够参考这里

 

Thrift架构

 

图中,TProtocol(协议层),定义数据传输格式,例如:

  • TBinaryProtocol:二进制格式;
  • TCompactProtocol:压缩格式;
  • TJSONProtocol:JSON格式;
  • TSimpleJSONProtocol:提供JSON只写协议, 生成的文件很容易经过脚本语言解析;
  • TDebugProtocol:使用易懂的可读的文本格式,以便于debug

TTransport(传输层),定义数据传输方式,能够为TCP/IP传输,内存共享或者文件共享等)被用做运行时库。

  • TSocket:阻塞式socker;
  • TFramedTransport:以frame为单位进行传输,非阻塞式服务中使用;
  • TFileTransport:以文件形式进行传输;
  • TMemoryTransport:将内存用于I/O,java实现时内部实际使用了简单的ByteArrayOutputStream;
  • TZlibTransport:使用zlib进行压缩, 与其余传输方式联合使用,当前无java实现;

 

Thrift支持的服务模型

  • TSimpleServer:简单的单线程服务模型,经常使用于测试;
  • TThreadPoolServer:多线程服务模型,使用标准的阻塞式IO;
  • TNonblockingServer:多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式);

 

Thrift其实是实现了C/S模式,经过代码生成工具将thrift文生成服务器端和客户端代码(能够为不一样语言),从而实现服务端和客户端跨语言的支持。用户在Thirft文件中声明本身的服务,这些服务通过编译后会生成相应语言的代码文件,而后客户端调用服务,服务器端提服务即可以了。

 

通常将服务放到一个.thrift文件中,服务的编写语法与C语言语法基本一致,在.thrift文件中有主要有如下几个内容:变量声明(variable)、数据声明(struct)和服务接口声明(service, 能够继承其余接口)。

下面分析Thrift的tutorial中带的例子tutorial.thrift:

复制代码
// 包含头文件
include “shared.thrift”        

// 指定目标语言
namespace cpp tutorial            

// 定义变量
const i32 INT32CONSTANT = 9853        

// 定义结构体
struct Work {
  1: i32 num1 = 0,
  2: i32 num2,
  3: Operation op,
  4: optional string comment,
}

// 定义服务
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.
    */
   oneway void zip()

}
复制代码

 

 

编译thrift文件,生成C++代码:

./thrift --gen cpp tutorial.thrift   #结果代码存放在gen-cpp目录下

 

若是是要生成java代码:

./thrift --gen java tutorial.thrift  #结果代码存放在gen-java目录下

 

client端和sever端代码要调用编译.thrift生成的中间文件。
下面分析cpp文件下面的CppClient.cpp和CppServer.cpp代码

 

 

 

在client端,用户自定义CalculatorClient类型的对象(用户在.thrift文件中声明的服务名称是Calculator, 则生成的中间代码中的主类为CalculatorClient), 该对象中封装了各类服务,能够直接调用(如client.ping()), 而后thrift会经过封装的rpc调用server端同名的函数。
在server端,须要实如今.thrift文件中声明的服务中的全部功能,以便处理client发过来的请求。

 


 

Thrift语法

Thrift文件支持shell命令,所以thrift是可执行的。

Thrfit支持shell注释风格(#),也支持C/C++语言中单行(//)或者多行(/* */)注释风格

 

数据类型

一、基本类型

  • bool,布尔型,1个字节;
  • byte,有符号单字节;
  • i16,有符号16位整型;
  • i32,有符号32位整型;
  • i64,有符号64位整型;
  • double,64位浮点数;
  • string,字符串;
  • binary,字节数组;

注意:thrift不支持无符号整型。

 

二、容器

  • map<t1,t2>,字典;
  • list<t1>,列表;
  • set<t1>,集合;

注意:容器中的元素类型能够是除了service 之外的任何合法thrift类型(包括结构体和异常)。

 

三、结构体 struct

Thrift结构体在概念上同C语言结构体类型—-一种将相关属性汇集(封装)在一块儿的方式;

在面向对象语言中,thrift结构体被转换成类。

struct Work {
  1: i32 num1 = 0,
  2: i32 num2,
  3: Operation op, 
  4: optional string comment,
}

结构体中,每一个字段包含一个整数ID,数据类型、字段名,和一个可选的默认值。

字段还能够声明为"optional",当该字段没有设置的时候,不会被序列化输出;

规范的struct定义中的每一个域均会使用required或者optional关键字进行标识。若是required标识的域没有赋值,thrift将给予提示。若是optional标识的域没有赋值,该域将不会被序列化传输。若是某个optional标识域有缺省值而用户没有从新赋值,则该域的值一直为缺省值。

 

四、异常 exception

异常在语法和功能上相似于结构体,只不过异常使用关键字exception而不是struct关键字声明。但它在语义上不一样于结构体,当定义一个RPC服务时,开发者可能须要声明一个远程方法抛出一个异常。

exception InvalidOperation {
  1: i32 what,
  2: string why 
}

 

五、服务 service 

在流行的序列化/反序列化框架(如protocol buffer)中,Thrift是少有的提供多语言间RPC服务的框架。

Thrift编译器会根据选择的目标语言为server产生服务接口代码,为client产生桩代码。

复制代码
//“Twitter”与“{”之间须要有空格!!!
service Twitter {
 
// 方法定义方式相似于C语言中的方式,它有一个返回值,一系列参数和可选的异常
 
// 列表. 注意,参数列表和异常列表定义方式与结构体中域定义方式一致.
 
void ping(),                       // 函数定义可使用逗号或者分号标识结束
 
bool postTweet(1:Tweet tweet);    // 参数能够是基本类型或者结构体,参数是只读的(const),不能够做为返回值!!!
 
TweetSearchResult searchTweets(1:string query); // 返回值能够是基本类型或者结构体
 
// ”oneway”标识符表示client发出请求后没必要等待回复(非阻塞)直接进行下面的操做,
 
// ”oneway”方法的返回值必须是void
 
oneway void zip()               // 返回值能够是void
 
}
复制代码

service中的函数,其参数列表的定义方式与struct彻底同样;

service支持继承,一个service可以使用extends关键字继承另外一个service,struct不支持继承;

 

六、枚举类型 enum

复制代码
enum TweetType {
 
TWEET,         // 编译器默认从1开始赋值
RETWEET = 2,  // 能够赋予某个常量某个整数
DM = 0xa,     //容许常量是十六进制整数
REPLY         // 末尾没有逗号
}        

struct Tweet { 1: required i32 userId; 2: required string userName; 3: required string text; 4: optional Location loc; 5: optional TweetType tweetType = TweetType.TWEET // 给常量赋缺省值时,使用常量的全称 16: optional string language = "english" }
复制代码

注意:枚举常量必须是32位的正整数

 

七、常量 const

Thrift容许用户定义常量,复杂的类型和结构体可以使用JSON形式表示。

const i32 INT_CONST = 1234;    // 分号是可选的
const map<string,string> MAP_CONST = {"hello": "world", "goodnight": "moon"}

 

PS:跟C语言相似,Thrift也支持typedef语句,例如:

typedef i32 MyInteger 

 

命名空间

Thrift中的命名空间同C++中的namespace相似,它们均提供了一种组织(隔离)代码的方式。由于每种语言均有本身的命名空间定义方式(如python中有module),thrift容许开发者针对特定语言定义namespace:

namespace cpp com.example.project 
namespace java com.example.project 

 


 

产生代码

下面介绍Thrift产生各类目标语言代码的方式,

 

Thrift的网络栈以下所示:

 

Transport层提供了一个简单的网络读写抽象层。这使得thrift底层的transport从系统其它部分(如:序列化/反序列化)解耦。

如下是一些Transport接口提供的方法:

复制代码
open
close
read
write
listen
accept
flush
复制代码

 

Protocol抽象层定义了一种将内存中数据结构映射成可传输格式的机制。换句话说,Protocol定义了datatype怎样使用底层的Transport对本身进行编解码。所以,Protocol的实现要给出编码机制并负责对数据进行序列化。

Protocol接口的定义以下:

复制代码
writeMessageBegin(name, type, seq)
writeMessageEnd()
writeStructBegin(name)
writeStructEnd()
writeFieldBegin(name, type, id)
writeFieldEnd()
writeFieldStop()
writeMapBegin(ktype, vtype, size)
writeMapEnd()
writeListBegin(etype, size)
writeListEnd()
writeSetBegin(etype, size)
writeSetEnd()
writeBool(bool)
writeByte(byte)
writeI16(i16)
writeI32(i32)
writeI64(i64)
writeDouble(double)
writeString(string)
name, type, seq = readMessageBegin()
readMessageEnd()
name = readStructBegin()
readStructEnd()
name, type, id = readFieldBegin()
readFieldEnd()
k, v, size = readMapBegin()
readMapEnd()
etype, size = readListBegin()
readListEnd()
etype, size = readSetBegin()
readSetEnd()
bool = readBool()
byte = readByte()
i16 = readI16()
i32 = readI32()
i64 = readI64()
double = readDouble()
string = readString()
复制代码

 

Processor封装了从输入数据流中读数据和向数据数据流中写数据的操做。读写数据流用Protocol对象表示。Processor的结构体很是简单:

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

与服务相关的processor实现由编译器产生。Processor主要工做流程以下:从链接中读取数据(使用输入protocol),将处理受权给handler(由用户实现),最后将结果写到链接上(使用输出protocol)。

 

Server将以上全部特性集成在一块儿:
(1) 建立一个transport对象
(2) 为transport对象建立输入输出protocol
(3) 基于输入输出protocol建立processor
(4) 等待链接请求并将之交给processor处理

 

 

 

 

 

 

参考文档:

http://dongxicheng.org/search-engine/thrift-framework-intro/

http://dongxicheng.org/search-engine/thrift-guide/

http://dongxicheng.org/search-engine/thrift-internals/

http://dongxicheng.org/search-engine/thrift-bidirectional-async-rpc/

相关文章
相关标签/搜索