1、thrift的基本介绍
1.1 thrift的定义
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的配置方式和具体使用。
1.2 thrift架构
Thrift技术栈分层从下向上分别为:传输层(Transport Layer)、协议层(Protocol Layer)、处理(Processor Layer)和服务层(Server Layer)。
- 传输层(Transport Layer):传输层负责直接从网络中读取和写入数据,它定义了具体的网络传输协议;比如说TCP/IP传输等。
- 协议层(Protocol Layer):协议层定义了数据传输格式,负责网络传输数据的序列化和反序列化;比如说JSON、XML、二进制数据等。
- 处理层(Processor Layer):处理层是由具体的IDL(接口描述语言)生成的,封装了具体的底层网络传输和序列化方式,并委托给用户实现的Handler进行处理。
-
服务层(Server Layer):整合上述组件,提供具体的网络IO模型(单线程/多线程/事件驱动),形成最终的服务。
采用TCP/IP作为更底层的通信协议图:
1.3 thrift的基本特性
1.3.1 开发速度快
通过编写RPC接口Thrift IDL文件,利用编译生成器自动生成服务端骨架(Skeletons)和客户端桩(Stubs)。从而省去开发者自定义和维护接口编解码、消息传输、服务器多线程模型等基础工作。服务端:只需要按照服务骨架即接口,编写好具体的业务处理程序(Handler)即实现类即可。客户端:只需要拷贝IDL定义好的客户端桩和服务对象,然后就像调用本地对象的方法一样调用远端服务。
1.3.2 接口维护简单
通过维护Thrift格式的IDL(接口描述语言)文件(注意写好注释),即可作为给Client使用的接口文档使用,也自动生成接口代码,始终保持代码和文档的一致性。且Thrift协议可灵活支持接口的可扩展性。
1.3.3 学习成本低
因为其来自Google Protobuf开发团队,所以其IDL文件风格类似Google Protobuf,且更加易读易懂;特别是RPC服务接口的风格就像写一个面向对象的Class一样简单。初学者只需参照:http://thrift.apache.org/,一个多小时就可以理解Thrift IDL文件的语法使用。
1.3.4 多语言/跨语言支持
Thrift支持C++、 Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、JavaScript、Node.js、Smalltalk等多种语言,即可生成上述语言的服务器端和客户端程序。
1.3.5 稳定/广泛使用
Thrift在很多开源项目中已经被验证是稳定和高效的,例如Cassandra、Hadoop、HBase等;国外在Facebook中有广泛使用,国内包括百度、美团小米、和饿了么等公司。
2、thrift的基本语法
2.1 thrift的IDL介绍
Thrift是一个典型的CS(客户端/服务端)结构,客户端和服务端可以使用不同的语言开发。既然客户端和服务端能使用不同的语言开发,那么一定就要有一种中间语言来关联客户端和服务端的语言,这种语言就是IDL(InterfaceDescription Language)
Thrift 采用IDL(Interface Definition Language)来定义通用的服务接口,然后通过Thrift提供的编译器,可以将服务接口编译成不同语言编写的代码,通过这个方式来实现跨语言的功能
2.2 IDL的基本语法——基础类型
2.3 特殊类型、集合容器
binary: 未编码的字节序列,是string的一种特殊形式;这种类型主要是方便某些场景下JAVA调用。JAVA中对应的是java.nio.ByteBuffer类型,GO中是[]byte
目前有三种容器类型:
在使用容器类型时必须指定泛型,否则无法编译idl文件。其次,泛型中的基本类型,JAVA语言中会被替换为对应的包装类型。
2.4 常量及类型别名(Const&&Typedef)
//常量定义
const i32 MALE_INT = 1
const map<i32, string> GENDER_MAP = {1: "male", 2: "female"}
//某些数据类型比较长可以用别名简化
typedef map<i32, string> gmp
2.5 struct类型
在面向对象语言中,表现为“类定义”;在弱类型语言、动态语言中,表现为“结构/结构体”。定义格式如下
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有以下一些约束:
- struct不能继承,但是可以嵌套,不能嵌套自己
- 其成员都是有明确类型
- 成员是被正整数编号过的,其中的编号使不能重复的,这个是为了在传输过程中编码使用
- 成员分割符可以是逗号(,)或是分号(;),而且可以混用
- 字段会有optional和required之分和protobuf一样,但是如果不指定则为无类型–可以不填充该值,但是在序列化传输的时候也会序列化进去,optional是不填充则不序列化,required是必须填充也必须序列化。
- 每个字段可以设置默认值
- 同一文件可以定义多个struct,也可以定义在不同的文件,进行include引入
2.6 枚举、异常
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)
}
2.7 Service (服务定义类型)
service UserService {
User getById(1:i32 id)
bool isExist(1:string name)
}
编译后的内容
2.8 Namespace (名字空间)
Thrift中的命名空间类似于C++中的namespace和java中的package,它们提供了一种组织(隔离)代码的简便方式。名字空间也可以用于解决类型定义中的名字冲突。由于每种语言均有自己的命名空间定义方式(如python中有module), thrift允许开发者针对特定语言
定义namespace
namespace java com.yogurt.test
转化后
package com.yogurt.test
3、thrift的快速使用
3.1 环境待建
thrift编译器的安装
参考文档:https://thrift.apache.org/docs/install/
windows 安装
下载地址:https://thrift.apache.org/download
centos 安装
参考文档:https://thrift.apache.org/docs/install/centos.html
本人是Windows系统,所以这里搭建Windows环境
3.1.1 下载windows的文件,重命名文件为thrift.exe
3.1.2 环境变量配置
配置PATH路径,我的路径为:E:\installation\thrift
3.1.3 测试
安装成功!
3.2 快速入门尝试下
3.2.1 编写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
3.2.2 编写代码
客户端代码
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);
}
}
}
结果
文章来源:https://www.toymoban.com/news/detail-795336.html
这是在单线程场景下的,后续将详细介绍多线程下的RPC调用文章来源地址https://www.toymoban.com/news/detail-795336.html
到了这里,关于RPC框架之——thrift的深入理解(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!