Thrift 简单介绍

前言

最近小组组织了分享活动,记录下

正文

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.PNG

从上图可以看出, 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框架

目前的 RPC 框架大致有两种不同的侧重方向,一种偏重于服务治理,另一种偏重于跨语言调用。

  1. 服务治理型的 RPC 框架有Dubbo、Motan 等,这类的 RPC 框架的特点是功能丰富,提供高性能的远程调用以及服务发现和治理功能,适用于大型服务的微服务化拆分以及管理,对于特定语言(Java)的项目可以十分友好的透明化接入。但缺点是语言耦合度较高,跨语言支持难度较大。
  2. 跨语言调用型的 RPC 框架有 Thrift、gRPC 等,这一类的 RPC 框架重点关注于服务的跨语言调用,能够支持大部分的语言进行语言无关的调用,非常适合于为不同语言提供通用远程服务的场景。但这类框架没有服务发现相关机制,实际使用时一般需要代理层进行请求转发和负载均衡策略控制

RPC VS RESTFUL

RPC 的消息传输可以通过 TCP、UDP 或者 HTTP等。RPC 通过 HTTP 传输消息的时候和 RESTful的架构是类似的,但是也有不同。

  1. 从使用方面来看,RPC 的客户端和服务器端师紧耦合的。RESTful基于 http的语义操作资源,参数的顺序一般没有关系,也很容易的通过代理转换链接和资源位置,从这一点上来说,RESTful 更灵活。而且它们操作的对象不一样。 RPC 操作的是方法对象。 RESTful 操作的是资源(resource),而不是方法。
  2. 从性能角度看,使用Http时,Http本身提供了丰富的状态功能与扩展功能,但也正由于Http提供的功能过多,导致在网络传输时,需要携带的信息更多,从性能角度上讲,较为低效。而RPC服务网络传输上仅传输与业务内容相关的数据,传输数据更小,性能更高。

Thrift的协议栈结构

thrift.PNG

Thrift是一种C/S的架构体系.在最上层是用户自行实现的业务逻辑代码.第二层是由Thrift编译器自动生成的代码,主要用于结构化数据的解析,发送和接收。TServer主要任务是高效的接受客户端请求,并将请求转发Processor处理。Processor负责对客户端的请求做出响应,包括RPC请求转发,调用参数解析和用户逻辑调用,返回值写回等处理。从TProtocol以下部分是thirft的传输协议和底层I/O通信。TProtocol是用于数据类型解析的,将结构化数据转化为字节流给TTransport进行传输。TTransport是与底层数据传输密切相关的传输层,负责以字节流方式接收和发送消息体,不关注是什么数据类型。底层IO负责实际的数据传输,包括socket、文件和压缩数据流等。

数据类型
  1. Base Types:基本类型
  2. Struct:结构体类型
  3. Container:容器类型,即List、Set、Map
  4. Exception:异常类型
  5. Service: 定义对象的接口,和一系列方法
协议

Thrift可以让你选择客户端与服务端之间传输通信协议的类别,在传输协议上总体上划分为文本(text)和二进制(binary)传输协议, 为节约带宽,提供传输效率,一般情况下使用二进制类型的传输协议为多数,但有时会还是会使用基于文本类型的协议,这需要根据项目/产品中的实际需求:

  1. TBinaryProtocol – 二进制编码格式进行数据传输。
  2. TCompactProtocol – 这种协议非常有效的,使用Variable-Length Quantity (VLQ) 编码对数据进行压缩。
  3. TJSONProtocol – 使用JSON的数据编码协议进行数据传输。
  4. TSimpleJSONProtocol – 这种节约只提供JSON只写的协议,适用于通过脚本语言解析。
  5. TDebugProtocol – 在开发的过程中帮助开发人员调试用的,以文本的形式展现方便阅读。
传输层
  1. TSocket- 使用堵塞式I/O进行传输,也是最常见的模式。
  2. TFramedTransport- 使用非阻塞方式,按块的大小,进行传输,类似于Java中的NIO。
  3. TFileTransport- 顾名思义按照文件的方式进程传输,虽然这种方式不提供Java的实现,但是实现起来非常简单。
  4. TMemoryTransport- 使用内存I/O,就好比Java中的ByteArrayOutputStream实现。
  5. TZlibTransport- 使用执行zlib压缩,不提供Java的实现。
服务端类型
  1. TSimpleServer - 单线程服务器端使用标准的堵塞式I/O。
  2. TThreadPoolServer - 多线程服务器端使用标准的堵塞式I/O。
  3. TNonblockingServer – 多线程服务器端使用非堵塞式I/O,并且实现了Java中的NIO通道。

2. 为什么要用Thrift

应用

  1. Facebook的开源的日志收集系统(scribe: https://github.com/facebook/scribe)
  2. 淘宝的实时数据传输平台(TimeTunnel http://code.taobao.org/p/TimeTunnel/wiki/index)
  3. Evernote开放接口(https://github.com/evernote/evernote-thrift)
  4. Quora(http://www.quora.com/Apache-Thrift)
  5. HBase( http://abloz.com/hbase/book.html#thrift )

优缺点

  1. 支持非常多的语言绑定。
  2. thrift文件生成目标代码,简单易用。
  3. 数据结构与传输表现的分离,支持多种消息格式。
  4. 包含完整的客户端/服务端堆栈,可快速实现RPC。
  5. 支持同步和异步通信。
  6. 和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的协议栈,下面将进行具体介绍

推荐阅读更多精彩内容

  • 转自:http://blog.csdn.net/kesonyk/article/details/50924489 ...
    晴天哥_王志阅读 23,051评论 2 38
  • Thrift是什么? Thrift是Facebook于2007年开发的跨语言的rpc服框架,提供多语言的编译功能,...
    jiangmo阅读 8,035评论 0 6
  • 前言: 目前流行的服务调用方式有很多种,例如基于 SOAP 消息格式的 Web Service,基于 JSON 消...
    我是嘻哈大哥阅读 1,440评论 0 9
  • 这么晚了才洗漱完毕,孩子们因为太激动到现在还没有睡。女儿几天前就一直念叨着大姑姑什么时候回来,在家里住几天?...
    八七在路上阅读 142评论 1 0
  • 当过两年的毕业典礼志愿者,现场听过嘉诚哥两次的毕业赠言,感受到嘉诚哥在教育上的投入和真诚贡献。 今天,偶然看到厦大...
    Vankin_Hsieh阅读 147评论 0 0
  • 一条蜿蜒崎岖的山路充满着儿时满满的回忆,我的童年总在这条山路上奔跑(因为读书怕迟到,哥哥姐姐她们跑,我也跟着跑)现...
    糖糖减脂教练阅读 142评论 0 0