学习RPC框架-Thrift日志
前言
Thrift是一个轻量级、跨语言的远程服务调用框架,最初是由Facebook开发的,后面进入Apache开源项目。他通过自身的IDL中间语言,并借助代码生成引擎生成各种主流语言的RPC服务端/客户端模版代码。
Thrift 支持多种不同的变成语言。包括C++、Java、Python、PHP、Ruby等。
正文
Thrift
软件栈分层从下向上分别为:传输层(Transport Layer
)、协议层(Protocol Layer
)、处理层(Processor Layer
)和服务层(Server Layer
)。
- 传输层(
Transport Layer
):传输层负责直接从网络中读取和写入数据,它定义了具体的网络传输协议;比如说TCP/IP
传输等。 - 协议层(
Protocol Layer
):协议层定义了数据传输格式,负责网络传输数据的序列化和反序列化;比如说JSON
、XML
、二进制数据等。 - 处理层(
Processor Layer
):处理层是由具体的IDL
(接口描述语言)生成的,封装了具体的底层网络传输和序列化方式,并委托给用户实现的Handler
进行处理。 - 服务层(
Server Layer
):整合上述组件,提供具体的网络线程/IO服务模型,形成最终的服务。
特点:
? 开发速度快
? 接口维护简单
? 多语言支持
? 稳定、使用广泛
数据类型
IDL是一个典型的CS结构,使用与客户端和服务端使用不同语言的调用。
基本类型、特殊类型、集合类型
类型 | 标志 | 描述 |
---|---|---|
基本类型 | bool | 布尔值 |
byte | 8位有符号整数 | |
i16 | 16位有符号整数 | |
i32 | 32位有符号整数 | |
i64 | 64位有符号整数 | |
double | 64位浮点数 | |
string | UTF-8编码的字符串 | |
枚举类型 | enum | 枚举类型 |
结构体类型 | struct | 定义的结构体对象 |
容器类型 | list | 有序元素列表 |
set | 无序不重复元素集合 | |
map | 无序的key/value集合 | |
特殊类型 | binary | 二进制串 |
异常类型 | exception | 异常类型 |
服务类型 | service | 具体对应服务的类 |
struct有以下一些约束:
- struct不能继承,但是可以嵌套,不能嵌套自己
- 其成员都是有明确类型
- 成员是被正整数编号过的,其中的编号使不能重复的,这个是为了在传输过程中编码使用
- 成员分割符可以是逗号(,)或是分号(;),而且可以混用
- 字段会有optional和required之分和protobuf一样,但是如果不指定则为无类型–可以不填充该值,但是在序列化传输的时候也会序列化进去,optional是不填充则不序列化,required是必须填充也必须序列化。
- 每个字段可以设置默认值
- 同一文件可以定义多个struct,也可以定义在不同的文件,进行include引入
下面来看看各类型是怎么定义的:
//命名空间-表示java代码会生成路径位org.surpass
namespace java org.surpass
//命名空间-表示python代码会生成路径位org.surpass
namespace py org.surpass
//定义一个实体
struct User {
1:required i16 id;
2:string name;
3:optional bool isMan;
4:i16 age = "18";
}
//定义一个实体
//required 表示该字段必须存在或者有值
//optional 表示可选
struct Teacher {
1:required i16 id;
2:string name;
3:list<User> userList;
4:set<User> userSet;
5:map<i16, User> userMap;
}
//定义一个异常
exception BusException {
1:i32 errorCode;
2:string errorMsg;
}
//定义一个业务处理类
service Teach {
list<User> getUserList(1:i16 teacherId);
User getUser(1:string name);
map<i16, User> getUserMap() throws (1:BusException e);
}
//定义一个枚举类
enum UserEnum {
CLASS1 = 1;
CLASS2 = 2;
}
那么我们如何根据thrift文件生成java
和 python
代码?
下载链接:https://thrift.apache.org/docs/install/
下载对应平台的thrift可执行文件,并加入到path环境变量中,这样在终端命令行中任何路径都能使用。
我们使用以下命令:
thrift -gen java gen.thrift
thrift -gen py gen.thrift
结果生成的文件就是这个样子:
org
└─surpass
BusException.java
Teach.java
Teacher.java
User.java
UserEnum.java
**文件解析:业务处理类
Teach.java
**
Iface:服务端通过实现HelloWorldService.Iface
接口,向客户端的提供具体的同步业务逻辑。
AsyncIface:服务端通过实现HelloWorldService.Iface
接口,向客户端的提供具体的异步业务逻辑。
Client:客户端通过HelloWorldService.Client
的实例对象,以同步的方式访问服务端提供的服务方法。
AsyncClient:客户端通过HelloWorldService.AsyncClient
的实例对象,以异步的方式访问服务端提供的服务方法。
而 Teach
作为业务处理类,我们只要继承里面的接口并且实现方法,实现自己的处理逻辑即可。
//自己实现的接口逻辑
public class TeachImpl implements Teach.Iface {
@Override
public List<User> getUserList(short teacherId) throws TException {
return new ArrayList<User>(){{
add(new User());
}};
}
@Override
public User getUser(String name) throws TException {
return new User((short) 1, name, (short) 20);
}
@Override
public Map<Short, User> getUserMap() throws BusException, TException {
return null;
}
}
Thrift的协议
Thrift
可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本(text
)和二进制(binary
)传输协议。为节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议,这需要根据项目/产品中的实际需求。常用协议有以下几种:
- TBinaryProtocol:二进制编码格式进行数据传输
- TCompactProtocol:高效率的、密集的二进制编码格式进行数据传输
- TJSONProtocol: 使用
JSON
文本的数据编码协议进行数据传输 - TSimpleJSONProtocol:只提供
JSON
只写的协议,适用于通过脚本语言解析
Thrift的传输层
常用的传输层有以下几种:
- TSocket:使用阻塞式
I/O
进行传输,是最常见的模式 - TNonblockingTransport:使用非阻塞方式,用于构建异步客户端
- TFramedTransport:使用非阻塞方式,按块的大小进行传输,类似于
Java
中的NIO
Thrift服务端类型
- TSimpleServer:单线程服务器端,使用标准的阻塞式
I/O
- TThreadPoolServer:多线程服务器端,使用标准的阻塞式
I/O
- TNonblockingServer:单线程服务器端,使用非阻塞式
I/O
- THsHaServer:半同步半异步服务器端,基于非阻塞式
IO
读写和多线程工作任务处理 - TThreadedSelectorServer:多线程选择器服务器端,对
THsHaServer
在异步IO
模型上进行增强
示例
服务端
public class TeachServer {
public static void main(String[] args) throws TTransportException {
TServerSocket socket = new TServerSocket(9111);
Teach.Processor<TeachImpl> processor = new Teach.Processor<>(new TeachImpl());
TBinaryProtocol.Factory factory = new TBinaryProtocol.Factory();
TServer.Args arg = new TSimpleServer.Args(socket);
arg.processor(processor);
arg.protocolFactory(factory);
TSimpleServer server = new TSimpleServer(arg);
server.serve();
}
}
客户端
public class TeachClient {
public static void main(String[] args) throws TException {
//由于在本机测试所以为localhoust,注意端口要一致
TSocket tSocket = new TSocket("localhost", 9111);
TBinaryProtocol tBinaryProtocol = new TBinaryProtocol(tSocket);
Teach.Client client = new Teach.Client(tBinaryProtocol);
tSocket.open();
User user = client.getUser("张三");
System.out.println(user);
}
}
总结
今天先写到这里后续再补充。。。。。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!