# thrift-learn **Repository Path**: weishaoying/thrift-learn ## Basic Information - **Project Name**: thrift-learn - **Description**: thrift学习 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2018-09-10 - **Last Updated**: 2025-08-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # thrift-learn [MTThrift学习](https://gitee.com/weishaoying/thrift-learn/blob/master/thrift.md) *** [Thrift入门](https://www.cnblogs.com/cyfonly/p/6059374.html) RPC 是一种通过网络从远程计算机程序上请求服务的方式,它使开发网络分布式程序更加容易。 ### 不仅是个序列化工具 RPC框架 Thrift最初是由 Facebook 开发用做系统内各语言之间的 RPC 通信的一个可扩展且跨语言的软件框架,它结合了功能强大的软件堆栈和代码生成引擎,允许定义一个简单的定义文件中的数据类型和服务接口,以作为输入文件,编译器生成代码用来方便地生成RPC客户端和服务器通信的无缝跨编程语言。 Thrift是IDL 描述性语言的一个具体实现,适用于程序对程序静态的数据交换,需要先确定好数据结构。 Thrift是完全静态化的,**当数据结构发生变化时,必须重新编辑IDL文件**、代码生成再编译载入的流程,跟其他IDL工具相比较可以视为是 Thrift 的弱项。Thrift 适用于搭建大型数据交换及存储的通用工具,在大型系统中的内部数据传输上相对于 JSON 和 XML 无论在性能、传输大小上有明显的优势。 注意, Thrift 不仅仅是个高效的序列化工具,它是一个完整的RPC框架 体系! [下载thrift 0.9.3](http://archive.apache.org/dist/thrift/0.9.3/) [下载thrift 0.11.0](http://www.apache.org/dyn/closer.cgi?path=/thrift/0.11.0/thrift-0.11.0.exe) ![t1](https://images2015.cnblogs.com/blog/897247/201611/897247-20161113221012717-424130274.png) ### 调用流程 Thrift 服务端是如何启动并提供服务的? ![t2](https://images2015.cnblogs.com/blog/897247/201611/897247-20161113221511327-1851363586.png) Thrift客户端 ![t3](https://images2015.cnblogs.com/blog/897247/201611/897247-20161113221511327-1851363586.png) ### 数据类型 **基本类型:** + bool:布尔值 (true or false), one byte + byte:有符号字节 + i16:16位有符号整型 + i16:16位有符号整型 + i32:32位有符号整型 + i64:64位有符号整型 + double:64位浮点型 + string:未知编码或者二进制的字符串 **结构体和异常类型:** Thrift 结构体 (struct) 在概念上类似于 C 语言结构体类型,在 java 中 Thrift 结构体将会被转换成面向对象语言的类。struct 的定义如下: ```text struct UserDemo {   1: i32 id;   2: string name;   3: i32 age = 25;   4: string phone; } ``` struct 具有以下特性: + struct 不能继承,但是可以嵌套,不能嵌套自己 + 其成员都是有明确类型 + 成员是被正整数编号过的,其中的编号使不能重复的,这个是为了在传输过程中编码使用(详情往下看备注1) + 成员分割符可以是逗号(,)或是分号(;),而且可以混用,但是为了清晰期间,建议在定义中只使用一种,比如java学习者可以就使用逗号(;) + 字段会有optional和required之分(详情往下看备注2) + 每个字段可以设置默认值 + 同一文件可以定义多个struct,也可以定义在不同的文件,进行include引入 备注1:数字标签作用非常大,随着项目开发的不断发展,也许字段会有变化,但是建议不要轻易修改这些数字标签,修改之后如果没有同步客户端和服务器端会让一方解析出问题。 备注2:关于 struct 字段类型,规范的 struct 定义中的每个域均会使用 required 或者 optional 关键字进行标识,但是如果不指定则为无类型,可以不填充该值,但是在序列化传输的时候也会序列化进去。其中 optional 是不填充则不序列化,required 是必须填充也必须序列化。如果 required 标识的域没有赋值,Thrift 将给予提示;如果 optional 标识的域没有赋值,该域将不会被序列化传输;如果某个 optional 标识域有缺省值而用户没有重新赋值,则该域的值一直为缺省值;如果某个 optional 标识域有缺省值或者用户已经重新赋值,而不设置它的 __isset 为 true,也不会被序列化传输。 异常在语法和功能上相当于结构体,差别是异常使用关键字 exception 而不是 struct 声明。它在语义上不同于结构体:当定义一个 RPC 服务时,开发者可能需要声明一个远程方法抛出一个异常。 **容器类型** Thrift 容器与目前流行编程语言的容器类型相对应,有3种可用容器类型: + list:元素类型为t的有序表,容许元素重复。对应java的ArrayList + set:元素类型为t的无序表,不容许元素重复。对应java的HashSet + map:键类型为t,值类型为t的kv对,键不容许重复。对对应Java的HashMap 其中容器中元素类型可以是除了 service 外的任何合法 Thrift 类型(包括结构体和异常)。 ** 服务类型** 服务的定义方法在语义上等同于面向对象语言中的接口。Thrift 编译器会产生执行这些接口的 client 和 server 存根(详情下一节会具体描述)。下面我们就举个简单的例子解释 service 如何定义: ```text namespace java com.weishaoying.thrift struct QryResult { /** *返回码, 1成功,0失败 */ 1:i32 code; /** *响应信息 */ 2:string msg; } service TestQry{ /** * 测试查询接口,当qryCode值为1时返回"成功"的响应信息,qryCode值为其他值时返回"失败"的响应信息 * @param qryCode测试参数 */ QryResult qryTest(1:i32 qryCode) } ``` 在上面的例子中我们定义了一个 service类型的结构,里面包含两个方法的定义。 在定义 services 的时候,我们还需要了解一下规则: + 继承类必须实现这些方法 + 参数可以是基本类型或者结构体 + 所有的参数都是const类型,不能作为返回值 + 所有的参数都是const类型,不能作为返回值 + 返回值可以是void(oneway的返回值一定是void) + 服务支持继承,一个service可使用extends关键字继承另一个service + 服务不支持重载 除上面所提到的四大数据类型外,Thrift 还支持**枚举类型(enum)和常量类型(const)。** ### 第一个例子 目录结构: ![t0](https://images.gitee.com/uploads/images/2018/0910/205521_6bf05ad5_1333587.jpeg "t1.jpg") 执行命令: ```text D:\0_project2018\thrift-learn\src\main\resources> thrift-0.9.3.exe -r -gen java TestQry.thrift ``` 1. 定义服务的实现类 ```java public class QueryImpl implements TestQry.Iface { @Override public QryResult qryTest(int qryCode) throws TException { QryResult result = new QryResult(); if (qryCode == 1) { result.code = 1; result.msg = "success"; } else { result.code = 0; result.msg = "fail"; } return result; } } ``` 2. Server端: ```java public class ThriftServer { private final static int DEFAULT_PORT = 30001; private static TServer server = null; public static void main(String[] args) { try { TNonblockingServerSocket socket = new TNonblockingServerSocket(DEFAULT_PORT); TestQry.Processor processor = new TestQry.Processor(new QueryImpl()); TNonblockingServer.Args arg = new TNonblockingServer.Args(socket); arg.protocolFactory(new TBinaryProtocol.Factory()); arg.transportFactory(new TFramedTransport.Factory()); arg.processorFactory(new TProcessorFactory(processor)); server = new TNonblockingServer(arg); server.serve(); } catch (TTransportException e) { e.printStackTrace(); } } } ``` 3. Client端: ```java public class ThriftClient { private final static int DEFAULT_QRY_CODE = 1; public static void main(String[] args) { try { TTransport tTransport = getTTransport(); TProtocol protocol = new TBinaryProtocol(tTransport); TestQry.Client client = new TestQry.Client(protocol); QryResult result = client.qryTest(DEFAULT_QRY_CODE); System.out.println("----code=" + result.code + ", msg=" + result.msg); } catch (Exception e) { e.printStackTrace(); } } private static TTransport getTTransport() throws Exception { try { TTransport tTransport = getTTransport("127.0.0.1", 30001, 5000); if (!tTransport.isOpen()) { tTransport.open(); } return tTransport; } catch (Exception e) { e.printStackTrace(); } return null; } private static TTransport getTTransport(String host, int port, int timeout) { final TSocket tSocket = new TSocket(host, port, timeout); final TTransport transport = new TFramedTransport(tSocket); return transport; } } ```