最近再看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的顶级项目。
远程过程调用(Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。 比如 Java RMI(远程方法调用(Remote
Method Invocation)。能够让在某个java虚拟机上的对象像调用本地对象一样调用另一个java虚拟机中的对象上的方法)。
从上图可以看出, RPC 本身是 client-server模型,也是一种 request-response 协议。
有些实现扩展了远程调用的模型,实现了双向的服务调用,但是不管怎样,调用过程还是由一个客户端发起,服务器端提供响应,基本模型没有变化。
服务的调用过程为:
1. client调用client stub,这是一次本地过程调用
2. client stub将参数打包成一个消息,然后发送这个消息。打包过程也叫做 marshalling
3. client所在的系统将消息发送给server
4. server的的系统将收到的包传给server stub
5. server stub解包得到参数。 解包也被称作 unmarshalling
6. 最后server stub调用服务过程. 返回结果按照相反的步骤传给client
目前的 RPC 框架大致有两种不同的侧重方向,一种偏重于服务治理,另一种偏重于跨语言调用。
1. 服务治理型的 RPC 框架有Dubbo、Motan 等,这类的 RPC 框架的特点是功能丰富,提供高性能的远程调用以及服务发现和治理功能,适用于大型服务的微服务化拆分以及管理,对于特定语言(Java)的项目可以十分友好的透明化接入。但缺点是语言耦合度较高,跨语言支持难度较大。
2. 跨语言调用型的 RPC 框架有 Thrift、gRPC 等,这一类的 RPC 框架重点关注于服务的跨语言调用,能够支持大部分的语言进行语言无关的调用,非常适合于为不同语言提供通用远程服务的场景。但这类框架没有服务发现相关机制,实际使用时一般需要代理层进行请求转发和负载均衡策略控制
RPC 的消息传输可以通过 TCP、UDP 或者 HTTP等。RPC 通过 HTTP 传输消息的时候和 RESTful的架构是类似的,但是也有不同。
1. 从使用方面来看,RPC 的客户端和服务器端师紧耦合的。RESTful基于 http的语义操作资源,参数的顺序一般没有关系,也很容易的通过代理转换链接和资源位置,从这一点上来说,RESTful 更灵活。而且它们操作的对象不一样。 RPC 操作的是方法对象。 RESTful 操作的是资源(resource),而不是方法。
2. 从性能角度看,使用Http时,Http本身提供了丰富的状态功能与扩展功能,但也正由于Http提供的功能过多,导致在网络传输时,需要携带的信息更多,从性能角度上讲,较为低效。而RPC服务网络传输上仅传输与业务内容相关的数据,传输数据更小,性能更高。
Thrift是一种C/S的架构体系.在最上层是用户自行实现的业务逻辑代码.第二层是由Thrift编译器自动生成的代码,主要用于结构化数据的解析,发送和接收。TServer主要任务是高效的接受客户端请求,并将请求转发Processor处理。Processor负责对客户端的请求做出响应,包括RPC请求转发,调用参数解析和用户逻辑调用,返回值写回等处理。从TProtocol以下部分是thirft的传输协议和底层I/O通信。TProtocol是用于数据类型解析的,将结构化数据转化为字节流给TTransport进行传输。TTransport是与底层数据传输密切相关的传输层,负责以字节流方式接收和发送消息体,不关注是什么数据类型。底层IO负责实际的数据传输,包括socket、文件和压缩数据流等。
Thrift可以让你选择客户端与服务端之间传输通信协议的类别,在传输协议上总体上划分为文本(text)和二进制(binary)传输协议, 为节约带宽,提供传输效率,一般情况下使用二进制类型的传输协议为多数,但有时会还是会使用基于文本类型的协议,这需要根据项目/产品中的实际需求:
先配置好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.thriftgroupId>
<artifactId>libthriftartifactId>
<version>0.10.0version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.5version>
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(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的协议栈,下面将进行具体介绍