Apache的Thrift软件框架,是用来进行可伸缩的、跨语言的服务开发,它通过一个代码生成引擎来构建高效、无缝的服务,这些服务能够实现跨语言调度,目前支持的语言有: C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi 等。
目前官网最新的版本是v0.10.0。下面主要介绍基于mac os系统的Thrift的安装。windows系统可参考官网教程进行安装。
mac os官网提供的安装方法比较复杂,这里介绍下mac 下的home brew的安装方法。
打开终端,如果你的mac还没有安装home brew,那么先要安装home brew,使用以下命令:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
然后安装最新版本的Thrift:
brew install thrift
等待安装完成,然后输入以下命令,如果打印了Thrift的版本信息,表示安装成功:
thrift -version
Thrift的structs用来定义一个通用对象,但是没有继承关系。
Thrift的异常类型,除了是继承于静态异常基类以外,其他的跟struct是类似的。表示的是一个异常对象。
Thrift 的service类型相当于定义一个面向对象编程的一个接口。Thrift的编译器会根据这个接口定义来生成服务端和客户端的接口实现代码。
以下是data.thrift文件:
namespace java thrift.generated
namespace py py.thrift.generated
typedef i16 short
typedef i32 int
typedef i64 long
typedef bool boolean
typedef string String
struct Person {
1: optional String username,
2: optional int age,
3: optional boolean married
}
exception DataException {
1: optional String message,
2: optional String callStack,
3: optional String date
}
service PersonService {
Person getPersonByUsername(1: required String username) throws (1: DataException dataException),
void savePerson(1: required Person person) throws (1: DataException dataException)
}
生成java的客户端服务端代码:
thrift --gen java src/thrift/data.thrift
生成Python的客户端服务端代码:
thrift --gen py src/thrift/data.thrift
其他语言的生成也是类似。
以java作为服务端,java作为客户端以及Python作为客户端对服务端进行请求。
java服务端代码:
public class PersonServiceImpl implements PersonService.Iface {
@Override
public Person getPersonByUsername(String username) throws DataException, TException {
System.out.println("Got client param: " + username);
Person person = new Person();
person.setUsername(username);
person.setAge(20);
person.setMarried(false);
return person;
}
@Override
public void savePerson(Person person) throws DataException, TException {
System.out.println("Got client param: ");
System.out.println(person.getUsername());
System.out.println(person.getAge());
System.out.println(person.isMarried());
}
}
public class ThriftServer {
public static void main(String[] args) throws Exception {
TNonblockingServerSocket serverSocket = new TNonblockingServerSocket(8899);
THsHaServer.Args arg = new THsHaServer.Args(serverSocket).minWorkerThreads(2).maxWorkerThreads(4);
PersonService.Processor processor = new PersonService.Processor<>(new PersonServiceImpl());
arg.protocolFactory(new TCompactProtocol.Factory());
arg.transportFactory(new TFramedTransport.Factory());
arg.processorFactory(new TProcessorFactory(processor));
TServer server = new THsHaServer(arg);
System.out.println("Thrift Server Started!");
server.serve();
}
}
java客户端代码:
public class ThriftClient {
public static void main(String[] args) {
TTransport transport = new TFramedTransport(new TSocket("localhost", 8899), 600);
TProtocol protocol = new TCompactProtocol(transport);
PersonService.Client client = new PersonService.Client(protocol);
try {
transport.open();
Person person = client.getPersonByUsername("张三");
System.out.println(person.getUsername());
System.out.println(person.getAge());
System.out.println(person.isMarried());
System.out.println("------------");
Person person1 = new Person();
person1.setUsername("李四");
person1.setAge(30);
person1.setMarried(true);
client.savePerson(person1);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
transport.close();
}
}
}
python客户端代码:
__author__ = '作者'
from py.thrift.generated import PersonService
from py.thrift.generated import ttypes
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TCompactProtocol
import sys
reload(sys)
sys.setdefaultencoding('utf8')
try:
tSocket = TSocket.TSocket('localhost', 8899)
tSocket.setTimeout(600)
transport = TTransport.TFramedTransport(tSocket)
protocol = TCompactProtocol.TCompactProtocol(transport)
client = PersonService.Client(protocol)
transport.open()
person = client.getPersonByUsername('张三')
print person.username
print person.age
print person.married
print '------------------'
newPerson = ttypes.Person()
newPerson.username = '李四'
newPerson.age = 30
newPerson.married = True
client.savePerson(newPerson)
except Thrift.TException, tx:
print '%s' % tx.message
先运行服务端,然后再运行客户端,观察服务端和客户端的输出来理解下thrift的一个调用流程是什么样的。
如上图,客户端在进行远程方法调用时,首先是通过Thrift的编译器生成的客户端,将调用信息(方法名,参数信息)以指定的协议进行封装,而传输层TTransport是对协议层的封装进行处理(比如封装成帧frame),并通过网络发送出去。服务端这边流程跟客户端相反,收到客户端发过来的数据后,首先经过传输层对传过来的数据进行处理,然后使用特定的协议(跟客户端是一一对应的)进行解析,然后再通过生成的Processor调用用户编写的代码,如果有返回值的话,返回值以逆向的顺序,即通过协议层封装,然后传输层处理对数据进行发送,到了客户端那边就是对服务端返回的数据进行处理,使用特定协议进行解析,然后得到一个调用个的结果。
以上就是Thrift的RPC调用的一个完整流程。
Thrift之所以被称为一种高效的RPC框架,其中一个重要的原因就是它提供了高效的数据传输。
以下是Thrift的传输格式种类:
以上可以看到,在线上环境,使用TCompactProtocol格式效率是最高的,同等数据传输占用网络带宽是最少的。
Facebook开源了一个简化thrift java开发的一个库——swift。swift是一个易于使用的、基于注解的java库,主要用来创建thrift可序列化类型和服务。
github地址:https://github.com/facebook/swift
Thrift是一个跨语言的RPC框架,如果有跨语言交互的业务场景,Thrift可能是一个很好的选择。如果使用恰当,thrift将是一个非常高效的一个RPC框架。开发时应根据具体场景选择合适的协议,传输方式以及服务模型。缺点就是Thrift并没有像dubbo那样提供分布式服务的支持,如果要支持分布式,需要开发者自己去开发集成。