thrift的基本构架:

上图源自:http://jnb.ociweb.com/jnb/jnbJun2009.html

底层Underlying I/O以上的部分,都是由thrift编译器生成的代码,其中:

Your Code 这是根据thrift文件中定义的dto及service接口方法

FooService.Client及FooService.Processer是thrift生成的用于客户端及服务端的标准代码

Foo.read/write 参数对象及结果对象在传输时,最终需要在client、server间进行重写,红色框指的就是这个

TProtocal 指传输的内容是啥?(二进制?Json ? )由于TProtocal是一个抽象类,因此最终调用时,如果想从BinaryProtocal换成JsonProtocal,这部分代码也不用重新生成

TTransport 指用什么方式传输?(Scoket? Memory?File?)同样,TTransport是抽象类,运行时由具体子类决定运输方式

最底层的Underlying I/O则是依赖于各种语言的实现,负责底层的网络通讯,thrift最初是由c++写的,理论上讲,c++上的性能应该最好。

上一章的demo为例如,QueryParameter及DemoService的类图如下:

点击图片可以查看大图,从类图上看,大量使用了内部类(inner class),对于dto对象,内部类基本上分为_Fileds, Schema,SchemaFactory 三类,

_Fields 是一个枚举,罗列了dto的各种属性成员,

Schema 封装了write/read方法

SchemaFactory 是一个工厂,用于创建Schema实例

服务接口的类图,就有点复杂了,密密麻麻象蜘蛛网,除了刚才的三大类外,DemoService的Inner Class中还有Client、Processor等类,大家有兴趣可以慢慢看。

TProtocal : 传输的内容(即:What? )

从类图上看,支持 压缩格式、二进制格式、Json格式 等。

TTransport : 传输的方式(即:How? )

Thrift支持的传输方式非常多,从类的命名就能大概看出一二。

TServer: Server的类图如下

基本上分为二大类:一类是同步阻塞的Server,一类是非阻塞模式的Server,其中THsHaServer是一个Half-Sync/Half-Async 半同步,半异步的server

meta_data 元数据

类图中的xxxMetaData,基本对应了 列表、K-V映射、(无重复元素)集合、结构(即:类)、枚举以及字段的元数据信息。

Schema :对不同类型的TProtocal的读写操作,在这里抽象出来。

Variable-Length Quantity VLQ 变长编码:
Thirft采用TCompactProtocol序列化时之所以高效,跟VLQ变长编码有很大关系,直接借下面这张图来说吧:

整数106903,在java中我们知道int占用4个bytes,也就是32bit,高位字节如果不满,用0填充(最高位符号位除外), 这样的话,很多用0填充的高位字节位置其实是浪费的,VLQ的基本思路是将2进制每7位分组,这样106903的2进制就可以分成3组,然后每1组的最高位设为1或0,如果为1,表示相邻的下一个字节还有内容,要继续读取,如果该位置为0,则表示结束了。

这样的话,106903最终只需要3个字节就可以存储了,节省了1个字节。

上述这一堆概念在运行时,是如何串起来的呢?

可以从TServer的部分源码中略知一二:

public abstract class TServer {

  public static class Args extends AbstractServerArgs<Args> {
public Args(TServerTransport transport) {
super(transport);
}
} public static abstract class AbstractServerArgs<T extends AbstractServerArgs<T>> {
final TServerTransport serverTransport;
TProcessorFactory processorFactory;
TTransportFactory inputTransportFactory = new TTransportFactory();
TTransportFactory outputTransportFactory = new TTransportFactory();
TProtocolFactory inputProtocolFactory = new TBinaryProtocol.Factory();
TProtocolFactory outputProtocolFactory = new TBinaryProtocol.Factory(); public AbstractServerArgs(TServerTransport transport) {
serverTransport = transport;
} ... /**
* Core processor
*/
protected TProcessorFactory processorFactory_; /**
* Server transport
*/
protected TServerTransport serverTransport_; /**
* Input Transport Factory
*/
protected TTransportFactory inputTransportFactory_; /**
* Output Transport Factory
*/
protected TTransportFactory outputTransportFactory_; /**
* Input Protocol Factory
*/
protected TProtocolFactory inputProtocolFactory_; /**
* Output Protocol Factory
*/
protected TProtocolFactory outputProtocolFactory_; private boolean isServing; protected TServerEventHandler eventHandler_; // Flag for stopping the server
// Please see THRIFT-1795 for the usage of this flag
protected volatile boolean stopped_ = false; protected TServer(AbstractServerArgs args) {
processorFactory_ = args.processorFactory;
serverTransport_ = args.serverTransport;
inputTransportFactory_ = args.inputTransportFactory;
outputTransportFactory_ = args.outputTransportFactory;
inputProtocolFactory_ = args.inputProtocolFactory;
outputProtocolFactory_ = args.outputProtocolFactory;
}

从上述源码可以看出,TServer 中包含有input/output二类TProtocol,即:体现了 数据进来和出去时传输的格式(Binary? Json?...),另外还有input与output的Transport,即:数据进来和出去的时候,如何传输?(Scoket? File?...),另外还有一个Processor,其子类是通过IDL(thrift定义文件)生成的,运行时必须传递进来具体的子类。

这样,传递什么数据(what)?用什么方式传输(how)? 以及数据如何处理(process)?都有了

而且从源码中,可以发现默认的input/output Protocol都是BinaryProtocol(14,15行)。

Server端启动的时序图如下:

Client调用的时序图:

注:上面这二张序列图均出自https://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/

有了这些整体的概念后,上一篇中的示例,如果我们想换成TCompactProtocal,Client与Server的代码都得同步修改,这样二边才能一致:

Client端:

public class ThriftClient {

    public static void main(String[] args) {

        try {
TTransport transport;
transport = new TSocket("localhost", 9090);
transport.open(); //TProtocol protocol = new TBinaryProtocol(transport);
TProtocol protocol = new TCompactProtocol(transport);
DemoService.Client client = new DemoService.Client(protocol);
...

Server端:

    public static void simple(DemoService.Processor processor) {
try {
TServerTransport serverTransport = new TServerSocket(9090);
Args args = new Args(serverTransport);
args.outputProtocolFactory(new TCompactProtocol.Factory());
args.inputProtocolFactory(new TCompactProtocol.Factory());
TServer server = new TSimpleServer(args.processor(processor));
//TServer server = new TSimpleServer(new Args(serverTransport).processor(processor));
System.out.println("Starting the simple server...");
server.serve();
} catch (Exception e) {
e.printStackTrace();
}
}

最后,对TCompactProtocol、TBinaryProtocol、TJSONProtocol这三种序列化协议简单测试下效率:

    @Test
public void testProtocol() throws Exception {
QueryParameter parameter = getQueryParameter(); //CompactProtocol测试
TSerializer serializerCompact = new TSerializer(new TCompactProtocol.Factory());
byte[] bytes1 = serializerCompact.serialize(parameter);
System.out.println("CompactProtocol序列后的byte数组长度:" + bytes1.length); //BinaryProtocol测试
TSerializer serializerBinary = new TSerializer(new TBinaryProtocol.Factory());
byte[] bytes2 = serializerBinary.serialize(parameter);
System.out.println("BinaryProtocol序列后的byte数组长度:" + bytes2.length); //JsonProtocol测试
TSerializer serializerJson = new TSerializer(new TJSONProtocol.Factory());
byte[] bytes3 = serializerJson.serialize(parameter);
System.out.println("JsonProtocol序列后的byte数组长度:" + bytes3.length);
System.out.println(serializerJson.toString(parameter)); System.out.println("-----------");
TDeserializer deserializerCompact = new TDeserializer(new TCompactProtocol.Factory());
QueryParameter query1 = new QueryParameter();
deserializerCompact.deserialize(query1, bytes1);
System.out.println("CompactProtocol反序列化结果:" + query1.equals(parameter)); TDeserializer deserializerBinary = new TDeserializer(new TBinaryProtocol.Factory());
QueryParameter query2 = new QueryParameter();
deserializerBinary.deserialize(query2, bytes2);
System.out.println("BinaryProtocol反序列化结果:" + query2.equals(parameter)); TDeserializer deserializerJSON = new TDeserializer(new TJSONProtocol.Factory());
QueryParameter query3 = new QueryParameter();
deserializerJSON.deserialize(query3, bytes3);
System.out.println("JSONProtocol反序列化结果:" + query2.equals(parameter));
} private QueryParameter getQueryParameter(){
QueryParameter query = new QueryParameter();
short start = 1;
short end = 5;
query.setAgeStart(start);
query.setAgeEnd(end);
return query;
}

输出结果:

CompactProtocol序列后的byte数组长度:5
BinaryProtocol序列后的byte数组长度:11
JsonProtocol序列后的byte数组长度:29
{"1":{"i16":1},"2":{"i16":5}}
-----------
CompactProtocol反序列化结果:true
BinaryProtocol反序列化结果:true
JSONProtocol反序列化结果:true

TCompactProtocol优势明显,序列后的bytes长度只有JSON的1/5左右,可以大幅减少网络传输量。

参考文章:

http://dongxicheng.org/search-engine/thrift-rpc/

http://blog.chinaunix.net/uid-20357359-id-2876170.html

http://jnb.ociweb.com/jnb/jnbJun2009.html

https://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/

http://www.cnblogs.com/brucewoo/tag/Thrift/

rpc框架之 thrift 学习 2 - 基本概念的更多相关文章

  1. rpc框架之 thrift 学习 1 - 安装 及 hello world

    thrift是一个facebook开源的高效RPC框架,其主要特点是跨语言及二进制高效传输(当然,除了二进制,也支持json等常用序列化机制),官网地址:http://thrift.apache.or ...

  2. rpc框架之 thrift连接池实现

    接前一篇rpc框架之HA/负载均衡构架设计 继续,写了一个简单的thrift 连接池: 先做点准备工作: package yjmyzz; public class ServerInfo { publi ...

  3. rpc框架之gRPC 学习 - hello world

    grpc是google在github于2015年开源的一款RPC框架,虽然protobuf很早google就开源了,但是google一直没推出正式的开源框架,导致github上基于protobuf的r ...

  4. RPC框架之Thrift

    目前流行的服务调用方式有很多种,例如基于SOAP消息格式的 Web Service,基于 JSON 消息格式的 RESTful 服务等.其中所用到的数据传输方式包括 XML,JSON 等,然而 XML ...

  5. RPC框架之Thrift分析(转)

    一.简介 1.Thrift是Facebook开发的跨语言的RPC服务框架.随后贡献给Apache开源组织.成为RPC服务的主流框架.   2.特点:  优点:       跨语言,支持java.c/c ...

  6. rpc框架之 avro 学习 2 - 高效的序列化

    同一类框架,后出现的总会吸收之前框架的优点,然后加以改进,avro在序列化方面相对thrift就是一个很好的例子.借用Apache Avro 与 Thrift 比较 一文中的几张图来说明一下,avro ...

  7. rpc框架之avro 学习 1 - hello world

    avro是hadoop的一个子项目,提供的功能与thrift.Protocol Buffer类似,都支持二进制高效序列化,也自带RPC机制,但是avro使用起来更简单,无需象thrift那样生成目标语 ...

  8. 服务化实战之 dubbo、dubbox、motan、thrift、grpc等RPC框架比较及选型

    转自: http://blog.csdn.net/liubenlong007/article/details/54692241 概述 前段时间项目要做服务化,所以我比较了现在流行的几大RPC框架的优缺 ...

  9. dubbo、dubbox、motan、thrift、grpc等RPC框架比较及选型

    概述 前段时间项目要做服务化,所以我比较了现在流行的几大RPC框架的优缺点以及使用场景,最终结合本身项目的实际情况选择了使用dubbox作为rpc基础服务框架.下面就简单介绍一下RPC框架技术选型的过 ...

随机推荐

  1. js window对象

    BOM的核心对象是window,它表示浏览器的一个实例. 在浏览器中,window对象是(1)通过JavaScript访问浏览器窗口的一个接口 (2)ECMAScript规定的Global对象 1.全 ...

  2. 16、总经理要阅读的书籍 - IT软件人员书籍系列文章

    总经理是公司的一个领导角色.他主要负责公司级别的比如规划,战略等等内容.有些公司的总经理,比如软件公司的总经理,往往是一个大的业务员,将更多的大型的软件项目投取过来,让公司能够有钱赚,让公司员工能够跟 ...

  3. js平滑返回顶部代码

    随便找的一个,使用时直接调用gotoTop就行了,至于调速度之类的我没试,有兴趣的自己试试吧 注意:如果你想改变这个函数的名称千万不要忘了要同时改变第37行的那个gotoTop /** * JavaS ...

  4. 全站HTTPs,没那么简单

    “全站 HTTPs”俨然成了目前的热门话题,很多网站都在摩拳擦掌要实行全站 HTTPs.凑巧,我们(沪江)也在推行这个计划. 一开始大家想得都很简单,把证书购买了.配好了,相应的路径改一改,就没有问题 ...

  5. SQL Server调优系列基础篇(索引运算总结)

    前言 上几篇文章我们介绍了如何查看查询计划.常用运算符的介绍.并行运算的方式,有兴趣的可以点击查看. 本篇将分析在SQL Server中,如何利用先有索引项进行查询性能优化,通过了解这些索引项的应用方 ...

  6. 模块module

    python中的Module相当于C++中头文件和命名空间的组合体,便于代码的组织,任何一个python代码的文件都是一个Module,都可以被其他模块import import,from...imp ...

  7. /etc/fstab 参数详解及如何设置开机自动挂载

    某些时候当Linux系统下划分了新的分区后,需要将这些分区设置为开机自动挂载,否则,Linux是无法使用新建的分区的. /etc/fstab 文件负责配置Linux开机时自动挂载的分区. Window ...

  8. android AsyncTask实例

    .java package com.example.activitydemoay; import android.app.Activity; import android.content.Intent ...

  9. mysql 分库分表

    分表是分散数据库压力的好方法. 分表,最直白的意思,就是将一个表结构分为多个表,然后,可以再同一个库里,也可以放到不同的库. 当然,首先要知道什么情况下,才需要分表.个人觉得单表记录条数达到百万到千万 ...

  10. 学习OpenStack之 (0):基础知识

    vi 方向键出现字母问题解决方法 执行命令 sudo apt-get remove vim-common 执行命令 sudo apt-get install vim 鼠标被virtualbox捕获无法 ...