文章目录- Thrift介绍
- Thrift的架构
- Thrift的特性
- 开发速度快
- 接口维护简单
- 学习成本低
- 多语言/跨语言支持
- 稳定/广泛使用
- 快速入门例子
- 编译安装
- 创建Thrift IDL文件
- 通过编译器编译user.thrift文件,生成java接口类文件
- 实现UserServiceService.Iface的定义方法
- 服务器端程序编写
- python客户端代码编写
- 开发速度快
- 接口维护简单
- 学习成本低
- 多语言/跨语言支持
- 稳定/广泛使用
- 编译安装
- 创建Thrift IDL文件
- 通过编译器编译user.thrift文件,生成java接口类文件
- 实现UserServiceService.Iface的定义方法
- 服务器端程序编写
- python客户端代码编写
Thrift介绍
Thrift是一个轻量级、跨语言的RPC框架,主要用于各个服务之间的RPC通信,它通过自身的IDL中间语言, 并借助代码生成引擎生成各种主流语言的RPC服务端/客户端模板代码。Thrift支持多种不同的编程语言,包括C , Java, Python, PHP等。
Thrift官网地址,以下内容基于Apache Thrift v0.15.0。
RPC 全称 Remote Procedure Call——远程过程调用。RPC技术简单说就是为了解决远程调用服务的一种技术,使得调用者像调用本地服务一样方便透明
Thrift是一个典型的CS(客户端/服务端)结构,客户端和服务端可以使用不同的语言开发。既然客户端和服务端能使用不同的语言开发,那么一定就要有一种中间语言来关联客户端和服务端的语言,这种语言就是IDL (InterfaceDescription Language)
Thrift的架构
Thrift技术栈分层从下向上分别为:传输层(Transport Layer)、协议层(Protocol Layer)、处理层(Processor Layer)和服务层(Server Layer)。最底层是IO层,如通过socket进行网络通信。
- 传输层(Transport Layer):传输层负责直接从网络中读取和写入数据,它定义了具体的网络传输协议。thrift传输层支持阻塞式IO和非阻塞式IO。
- 协议层(Protocol Layer):协议层定义了数据传输格式,负责网络传输数据的序列化和反序列化;比如说JSON、XML、二进制数据等。
- 处理层(Processor Layer):处理层是由具体的IDL(接口描述语言)生成的,封装了具体的底层网络传输和序列化方式,并委托给用户实现的Handler进行处理。
- 服务层(Server Layer):整合上述组件,提供具体的网络IO模型(单线程/多线程/事件驱动),形成最终的服务。即业务逻辑代码。
Thrift的特性
开发速度快
通过编写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中有广泛使用,国内包括百度、美团小米、和饿了么等公司。
快速入门例子
编写user.thrift,通过IDL中间语言生成java代码,python代码,结合生成的代码,编写java服务端和python客户端,实现python跨语言调用java服务端接口代码。
编译安装
thrift编译器的安装 参考文档:https://thrift.apache.org/docs/install/ windows 安装 下载地址:https://thrift.apache.org/download centos 安装 参考文档:https://thrift.apache.org/docs/install/centos.html
安装好后,配置好环境变量,测试安装是否成功:
代码语言:javascript复制#可以通过以下命令查看生成命令的格式
thrift -help
创建Thrift IDL文件
代码语言:javascript复制namespace java com.test
namespace py com.test
struct User{
1:i32 id
2:string name
3:i32 age=0
}
service UserService {
User getById(1:i32 id)
bool isExist(1:string name)
}
通过编译器编译user.thrift文件,生成java接口类文件
代码语言:javascript复制# 编译user.thrift
thrift -gen java user.thrift
生成UserService.java、User.java
User.java:
代码语言:javascript复制@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.15.0)", date = "2022-01-14")
public class User implements org.apache.thrift.TBase<User, User._Fields>, java.io.Serializable, Cloneable, Comparable<User> {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("User");
private static final org.apache.thrift.protocol.TField ID_FIELD_DESC = new org.apache.thrift.protocol.TField("id", org.apache.thrift.protocol.TType.I32, (short)1);
private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)2);
private static final org.apache.thrift.protocol.TField AGE_FIELD_DESC = new org.apache.thrift.protocol.TField("age", org.apache.thrift.protocol.TType.I32, (short)3);
private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new UserStandardSchemeFactory();
private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new UserTupleSchemeFactory();
...
UserService.java:
代码语言:javascript复制@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.15.0)", date = "2022-01-14")
public class UserService {
public interface Iface {
public User getById(int id) throws org.apache.thrift.TException;
public boolean isExist(java.lang.String name) throws org.apache.thrift.TException;
}
...
实现UserServiceService.Iface的定义方法
将生成类的UserService.java、User.java源文件拷贝进项目源文件目录中,并实现UserServiceService.Iface的定义的getById()方法。
代码语言:javascript复制package com.test.service;
import org.apache.thrift.TException;
import com.test.User;
import com.test.UserService;
public class UserServiceImpl implements UserService.Iface {
@Override
public User getById(int id) throws TException {
System.out.println("=====调用getById=====");
//todo 模拟业务调用
User user = new User();
user.setId(id);
user.setName("fox");
user.setAge(30);
return user;
}
@Override
public boolean isExist(String name) throws TException {
return false;
}
}
服务器端程序编写
代码语言:javascript复制public class SimpleService {
public static void main(String[] args) {
try {
TServerTransport serverTransport = new TServerSocket(9090);
//获取processor
UserService.Processor processor = new UserService.Processor(new UserServiceImpl());
//指定TBinaryProtocol
TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory();
TSimpleServer.Args targs = new TSimpleServer.Args(serverTransport);
targs.processor(processor);
targs.protocolFactory(protocolFactory);
//单线程服务模型,一般用于测试
TServer server = new TSimpleServer(targs);
System.out.println("Starting the simple server...");
//暴露服务
server.serve();
} catch (Exception e) {
e.printStackTrace();
}
}
}
python客户端代码编写
- 通过编译器编译user.thrift文件,生成python代码然后将生成的 python 代码 和 文件,放到新建的 python 项目中
thrift -gen py user.thrift
- python中使用thrift需要安装thrift模块
pip install thrift
- 创建python客户端程序
from thrift.transport import TSocket, TTransport
from thrift.protocol import TBinaryProtocol
from com.tuling import UserService
# Make socket
transport = TSocket.TSocket('localhost', 9090)
transport.setTimeout(600)
# Buffering is critical. Raw sockets are very slow
transport = TTransport.TBufferedTransport(transport)
# Wrap in a protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# Create a client to use the protocol encoder
client = UserService.Client(protocol)
# Connect!
transport.open()
result = client.getById(1)
print(result)