前言
最近小组组织了分享活动,记录下
正文
1. 什么是Thrift
Thrift的起源
Thrift是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务。它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为“大规模跨语言服务开发”而开发的。它通过一个代码生成引擎联合了一个软件栈,来创建不同程度的、无缝的跨平台高效服务,可以使用C#、C++(基于POSIX兼容系统)Cappuccino、Cocoa、Delphi、Erlang、Go、Haskell、Java、Node.js、OCaml、Perl、PHP、Python、Ruby和Smalltalk编程语言开发。 2007由Facebook开源,2008年5月进入Apache孵化器, 2010年10月成为Apache的顶级项目。
什么是RPC
远程过程调用(Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。 比如 Java RMI(远程方法调用(Remote
Method Invocation)。能够让在某个java虚拟机上的对象像调用本地对象一样调用另一个java虚拟机中的对象上的方法)。
从上图可以看出, RPC 本身是 client-server模型,也是一种 request-response 协议。
有些实现扩展了远程调用的模型,实现了双向的服务调用,但是不管怎样,调用过程还是由一个客户端发起,服务器端提供响应,基本模型没有变化。
服务的调用过程为:
- client调用client stub,这是一次本地过程调用
- client stub将参数打包成一个消息,然后发送这个消息。打包过程也叫做 marshalling
- client所在的系统将消息发送给server
- server的的系统将收到的包传给server stub
- server stub解包得到参数。 解包也被称作 unmarshalling
- 最后server stub调用服务过程. 返回结果按照相反的步骤传给client
其他RPC框架
目前的 RPC 框架大致有两种不同的侧重方向,一种偏重于服务治理,另一种偏重于跨语言调用。
- 服务治理型的 RPC 框架有Dubbo、Motan 等,这类的 RPC 框架的特点是功能丰富,提供高性能的远程调用以及服务发现和治理功能,适用于大型服务的微服务化拆分以及管理,对于特定语言(Java)的项目可以十分友好的透明化接入。但缺点是语言耦合度较高,跨语言支持难度较大。
- 跨语言调用型的 RPC 框架有 Thrift、gRPC 等,这一类的 RPC 框架重点关注于服务的跨语言调用,能够支持大部分的语言进行语言无关的调用,非常适合于为不同语言提供通用远程服务的场景。但这类框架没有服务发现相关机制,实际使用时一般需要代理层进行请求转发和负载均衡策略控制
RPC VS RESTFUL
RPC 的消息传输可以通过 TCP、UDP 或者 HTTP等。RPC 通过 HTTP 传输消息的时候和 RESTful的架构是类似的,但是也有不同。
- 从使用方面来看,RPC 的客户端和服务器端师紧耦合的。RESTful基于 http的语义操作资源,参数的顺序一般没有关系,也很容易的通过代理转换链接和资源位置,从这一点上来说,RESTful 更灵活。而且它们操作的对象不一样。 RPC 操作的是方法对象。 RESTful 操作的是资源(resource),而不是方法。
- 从性能角度看,使用Http时,Http本身提供了丰富的状态功能与扩展功能,但也正由于Http提供的功能过多,导致在网络传输时,需要携带的信息更多,从性能角度上讲,较为低效。而RPC服务网络传输上仅传输与业务内容相关的数据,传输数据更小,性能更高。
Thrift的协议栈结构
Thrift是一种C/S的架构体系.在最上层是用户自行实现的业务逻辑代码.第二层是由Thrift编译器自动生成的代码,主要用于结构化数据的解析,发送和接收。TServer主要任务是高效的接受客户端请求,并将请求转发Processor处理。Processor负责对客户端的请求做出响应,包括RPC请求转发,调用参数解析和用户逻辑调用,返回值写回等处理。从TProtocol以下部分是thirft的传输协议和底层I/O通信。TProtocol是用于数据类型解析的,将结构化数据转化为字节流给TTransport进行传输。TTransport是与底层数据传输密切相关的传输层,负责以字节流方式接收和发送消息体,不关注是什么数据类型。底层IO负责实际的数据传输,包括socket、文件和压缩数据流等。
数据类型
- Base Types:基本类型
- Struct:结构体类型
- Container:容器类型,即List、Set、Map
- Exception:异常类型
- Service: 定义对象的接口,和一系列方法
协议
Thrift可以让你选择客户端与服务端之间传输通信协议的类别,在传输协议上总体上划分为文本(text)和二进制(binary)传输协议, 为节约带宽,提供传输效率,一般情况下使用二进制类型的传输协议为多数,但有时会还是会使用基于文本类型的协议,这需要根据项目/产品中的实际需求:
- TBinaryProtocol – 二进制编码格式进行数据传输。
- TCompactProtocol – 这种协议非常有效的,使用Variable-Length Quantity (VLQ) 编码对数据进行压缩。
- TJSONProtocol – 使用JSON的数据编码协议进行数据传输。
- TSimpleJSONProtocol – 这种节约只提供JSON只写的协议,适用于通过脚本语言解析。
- TDebugProtocol – 在开发的过程中帮助开发人员调试用的,以文本的形式展现方便阅读。
传输层
- TSocket- 使用堵塞式I/O进行传输,也是最常见的模式。
- TFramedTransport- 使用非阻塞方式,按块的大小,进行传输,类似于Java中的NIO。
- TFileTransport- 顾名思义按照文件的方式进程传输,虽然这种方式不提供Java的实现,但是实现起来非常简单。
- TMemoryTransport- 使用内存I/O,就好比Java中的ByteArrayOutputStream实现。
- TZlibTransport- 使用执行zlib压缩,不提供Java的实现。
服务端类型
- TSimpleServer - 单线程服务器端使用标准的堵塞式I/O。
- TThreadPoolServer - 多线程服务器端使用标准的堵塞式I/O。
- TNonblockingServer – 多线程服务器端使用非堵塞式I/O,并且实现了Java中的NIO通道。
2. 为什么要用Thrift
应用
- Facebook的开源的日志收集系统(scribe: https://github.com/facebook/scribe)
- 淘宝的实时数据传输平台(TimeTunnel http://code.taobao.org/p/TimeTunnel/wiki/index)
- Evernote开放接口(https://github.com/evernote/evernote-thrift)
- Quora(http://www.quora.com/Apache-Thrift)
- HBase( http://abloz.com/hbase/book.html#thrift )
优缺点
- 支持非常多的语言绑定。
- thrift文件生成目标代码,简单易用。
- 数据结构与传输表现的分离,支持多种消息格式。
- 包含完整的客户端/服务端堆栈,可快速实现RPC。
- 支持同步和异步通信。
- 和protobuf(谷歌的一种灵活高效的独立于语言平台的结构化数据表示方法)一样不支持动态特性
3. 如何使用Thrift
Thrift的简单demo
先配置好Thrift的环境,写一个hello.thrift,内容如下
namespace java service.demo
service Hello{
string helloWorld(1:string para)
}
执行如下命令生成java代码
thrift -r -gen java Hello.thrift
生成gen-java文件夹,将里面的Hello.java copy到工程里面
pom.xml文件添加依赖
<dependencies>
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
</dependencies>
实现类
package service.demo;
import org.apache.thrift.TException;
/**
* 1.服务实现类
*
* @author micheal
* @create 2018-03-26 11:03
**/
public class HelloServiceImpl implements Hello.Iface{
@Override
public String helloWorld(String para) throws TException {
return para+":helloWorld";
}
}
服务类
package service.demo;
import com.sun.org.apache.xpath.internal.SourceTree;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
/**
* 2.服务端
*
* @author micheal
* @create 2018-03-26 11:04
**/
public class HelloServiceServer {
public static void main(String[] args) throws TTransportException {
System.out.println("服务端开启......");
TProcessor tProcessor=new Hello.Processor<Hello.Iface>(new HelloServiceImpl());
TServerSocket serverSocket = new TServerSocket(8999);
TServer.Args tArgs = new TServer.Args(serverSocket);
tArgs.processor(tProcessor);
tArgs.protocolFactory(new TBinaryProtocol.Factory());
TServer server = new TSimpleServer(tArgs);
server.serve();
}
}
客户端
package service.demo;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
/**
* 3.客户端
*
* @author micheal
* @create 2018-03-26 11:44
**/
public class HelloServiceClient {
public static void main(String[] args) throws TException {
System.out.println("客户端启动.....");
TTransport transport =new TSocket("localhost", 8999, 30000);
// 协议要和服务端一致
TProtocol protocol = new TBinaryProtocol(transport);
Hello.Client client = new Hello.Client(protocol);
transport.open();
String result = client.helloWorld("TOM");
System.out.println(result);
}
}
先启动服务端,然后启动客户端
运行结果
客户端启动.....
Received 1
TOM:helloWorld
总结
本次简单的介绍,大概的了解下RPC框架的原理以及Thrift的协议栈,下面将进行具体介绍