赞
踩
Thrift是一个轻量级、跨语言的RPC框架,主要用于各个服务之间的RPC通信,最初由Facebook于2007年开发,2008年进入Apache开源项目。它通过自身的IDL中间语言, 并借助代码生成引擎生成各种主流语言的RPC服务端/客户端模板代码。Thrift支持多种不同的编程语言,包括C++, Java, Python,PHP,Ruby, Erlang, Haskell, C#, Cocoa, Javascript, Node.js, Smalltalk, OCaml, Golang等,本系列主要讲述基于Java语言的Thrift的配置方式和具体使用。
Thrift技术栈分层从下向上分别为:传输层(Transport Layer)、协议层(Protocol Layer)、处理(Processor Layer)和服务层(Server Layer)。
通过编写RPC接口Thrift IDL文件,利用编译生成器自动生成服务端骨架(Skeletons)和客户端桩(Stubs)。从而省去开发者自定义和维护接口编解码、消息传输、服务器多线程模型等基础工作。服务端:只需要按照服务骨架即接口,编写好具体的业务处理程序(Handler)即实现类即可。客户端:只需要拷贝IDL定义好的客户端桩和服务对象,然后就像调用本地对象的方法一样调用远端服务。
通过维护Thrift格式的IDL(接口描述语言)文件(注意写好注释),即可作为给Client使用的接口文档使用,也自动生成接口代码,始终保持代码和文档的一致性。且Thrift协议可灵活支持接口的可扩展性。
因为其来自Google Protobuf开发团队,所以其IDL文件风格类似Google Protobuf,且更加易读易懂;特别是RPC服务接口的风格就像写一个面向对象的Class一样简单。初学者只需参照:http://thrift.apache.org/,一个多小时就可以理解Thrift IDL文件的语法使用。
Thrift支持C++、 Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、JavaScript、Node.js、Smalltalk等多种语言,即可生成上述语言的服务器端和客户端程序。
Thrift在很多开源项目中已经被验证是稳定和高效的,例如Cassandra、Hadoop、HBase等;国外在Facebook中有广泛使用,国内包括百度、美团小米、和饿了么等公司。
Thrift是一个典型的CS(客户端/服务端)结构,客户端和服务端可以使用不同的语言开发。既然客户端和服务端能使用不同的语言开发,那么一定就要有一种中间语言来关联客户端和服务端的语言,这种语言就是IDL(InterfaceDescription Language)
Thrift 采用IDL(Interface Definition Language)来定义通用的服务接口,然后通过Thrift提供的编译器,可以将服务接口编译成不同语言编写的代码,通过这个方式来实现跨语言的功能
binary: 未编码的字节序列,是string的一种特殊形式;这种类型主要是方便某些场景下JAVA调用。JAVA中对应的是java.nio.ByteBuffer类型,GO中是[]byte
目前有三种容器类型:
在使用容器类型时必须指定泛型,否则无法编译idl文件。其次,泛型中的基本类型,JAVA语言中会被替换为对应的包装类型。
//常量定义
const i32 MALE_INT = 1
const map<i32, string> GENDER_MAP = {1: "male", 2: "female"}
//某些数据类型比较长可以用别名简化
typedef map<i32, string> gmp
在面向对象语言中,表现为“类定义”;在弱类型语言、动态语言中,表现为“结构/结构体”。定义格式如下
struct <结构体名称> {
<序号>:[字段性质] <字段类型> <字段名称> [= <默认值>] [;|,]
}
例如:
struct User{
1: required string name, //该字段必须填写
2: optional i32 age = 0; //默认值
3: bool gender //默认字段类型为optional
}
struct bean{
1: i32 number=10,
2: i64 bigNumber,
3: double decimals,
4: string name="thrifty"
}
struct有以下一些约束:
Thrift不支持枚举类嵌套,枚举常量必须是32位的正整数
enum HttpStatus {
OK = 200,
NOTFOUND=404
}
异常在语法和功能上类似于结构体,差别是异常使用关键字exception,而且异常是继承每种语言的基础异常类
exception MyException {
1: i32 errorCode
2: string message
}
service ExampleService {
string GetName() throws (1: MyException e)
}
service UserService {
User getById(1:i32 id)
bool isExist(1:string name)
}
编译后的内容
Thrift中的命名空间类似于C++中的namespace和java中的package,它们提供了一种组织(隔离)代码的简便方式。名字空间也可以用于解决类型定义中的名字冲突。由于每种语言均有自己的命名空间定义方式(如python中有module), thrift允许开发者针对特定语言
定义namespace
namespace java com.yogurt.test
转化后
package com.yogurt.test
thrift编译器的安装
参考文档:https://thrift.apache.org/docs/install/
windows 安装
下载地址:https://thrift.apache.org/download
centos 安装
参考文档:https://thrift.apache.org/docs/install/centos.html
本人是Windows系统,所以这里搭建Windows环境
配置PATH路径,我的路径为:E:\installation\thrift
安装成功!
namespace java com.yogurt
struct User{
1:i32 id
2:string name
3:i32 age=0
}
service UserService {
User getById(1:i32 id)
bool isExist(1:string name)
}
Thrift的相关命令:
# 生成java thrift -gen java user.thrift # 生成c++ thrift -gen cpp user.thrift # 生成php thrift -gen php user.thrift # 生成node.js thrift -gen js:node user.thrift #可以通过以下命令查看生成命令的格式 thrift -help //指定输出目录 thrift --gen java -o target user.thrift
进行编译:
默认会放在当前路径下,我当前的路径是/User/asus
客户端代码
package com.yogurt; import com.yogurt.Service.User; import com.yogurt.Service.UserService; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.transport.TSocket; /** * @author yogurt * @Date 2023/2/26 - 18:36 - 2023 */ public class client { public static void main(String[] args) { try{ TSocket socket = new TSocket("localhost", 9000); // 指定二进制编码 TBinaryProtocol protocol = new TBinaryProtocol(socket); UserService.Client client = new UserService.Client(protocol); socket.open(); // RPC 调用 User byId = client.getById(9000); System.out.println("byId = " + byId); }catch (Exception e){ System.out.println(e); } } }
服务端代码
package com.yogurt; import com.yogurt.Service.Impl.UserServiceImpl; import com.yogurt.Service.UserService; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TSimpleServer; import org.apache.thrift.transport.TServerSocket; /** * @author yogurt * @Date 2023/2/26 - 18:27 - 2023 */ public class SimpleService { public static void main(String[] args) { try{ TServerSocket socket = new TServerSocket(9000); // 获取process UserService.Processor processor = new UserService.Processor(new UserServiceImpl()); // 指定TBinaryProtocol TBinaryProtocol.Factory factory = new TBinaryProtocol.Factory(); TServer.Args args1 = new TSimpleServer.Args(socket); args1.processor(processor); args1.protocolFactory(factory); TSimpleServer tSimpleServer = new TSimpleServer(args1); tSimpleServer.serve(); }catch (Exception e){ System.out.println(e); } } }
结果
这是在单线程场景下的,后续将详细介绍多线程下的RPC调用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。