[thrift] thrift基本原理及使用
参考文章RPC 基本原理与 Apach Thrift 初体验
RPC基本原理
RPC(Remote Procedure Call),远程过程调用,大部分的RPC框架都遵循如下三个开发步骤:
1. 定义一个接口说明文件:描述了对象(结构体)、对象成员、接口方法等一系列信息;
2. 通过RPC框架所提供的编译器,将接口说明文件编译成具体的语言文件;
3. 在客户端和服务器端分别引入RPC编译器所生成的文件,即可像调用本地方法一样调用服务端代码;
RPC通信过程如下图所示
通信过程包括以下几个步骤:
1、客户过程以正常方式调用客户桩(client stub,一段代码);
2、客户桩生成一个消息,然后调用本地操作系统;
3、客户端操作系统将消息发送给远程操作系统;
4、远程操作系统将消息交给服务器桩(server stub,一段代码);
5、服务器桩将参数提取出来,然后调用服务器过程;
6、服务器执行要求的操作,操作完成后将结果返回给服务器桩;
7、服务器桩将结果打包成一个消息,然后调用本地操作系统;
8、服务器操作系统将含有结果的消息发送回客户端操作系统;
9、客户端操作系统将消息交给客户桩;
10、客户桩将结果从从消息中提取出来,返回给调用它的客户过程;
所有这些步骤的效果是,将客户过程对客户桩发出的本地调用转换成对服务器过程的本地调用,而客户端和服务器都不会意识到有中间步骤的存在。
这个时候,你可能会想,既然是调用另一台机器的服务,使用 RESTful API 也可以实现啊,为什么要选择 RPC 呢?我们可以从两个方面对比:
- 资源粒度。RPC 就像本地方法调用,RESTful API 每一次添加接口都可能需要额外地组织开放接口的数据,这相当于在应用视图中再写了一次方法调用,而且它还需要维护开发接口的资源粒度、权限等;
- 流量消耗。RESTful API 在应用层使用 HTTP 协议,哪怕使用轻型、高效、传输效率高的 JSON 也会消耗较大的流量,而 RPC 传输既可以使用 TCP 也可以使用 UDP,而且协议一般使用二制度编码,大大降低了数据的大小,减少流量消耗。
对接异构第三方服务时,通常使用 HTPP/RESTful 等公有协议,对于内部的服务调用,应用选择性能更高的二进制私有协议。
Thrift架构
thrift主要用于各个服务之间的RPC通信,支持跨语言。thrift是一个典型的CS结构,客户端和服务端可以使用不同的语言开发,thrift通过IDL(Interface Description Language)来关联客户端和服务端。thrift的整体架构图如下图所示
图中Your Code是用户实现的业务逻辑,接下来的FooService.Client和Foo.write()/read()是thrift根据IDL生成的客户端和服务端的代码,对应于RPC中Client stub和Server stub。TProtocol 用来对数据进行序列化与反序列化,具体方法包括二进制,JSON 或者 Apache Thrift 定义的格式。TTransport 提供数据传输功能,使用 Apache Thrift 可以方便地定义一个服务并选择不同的传输协议。
如下图所示为thrift的网络栈结构
thirft使用socket进行数据传输,数据以特定的格式发送,接收方进行解析。我们定义好thrift的IDL文件后,就可以使用thrift的编译器来生成双方语言的接口、model,在生成的model以及接口代码中会有解码编码的代码。
TTransport层
代表thrift的数据传输方式,thrift定义了如下几种常用数据传输方式
TSocket: 阻塞式socket;TFramedTransport: 以frame为单位进行传输,非阻塞式服务中使用;TFileTransport: 以文件形式进行传输;
TProtocol层
代表thrift客户端和服务端之间传输数据的协议,通俗来讲就是客户端和服务端之间传输数据的格式(例如json等),thrift定义了如下几种常见的格式
TBinaryProtocol: 二进制格式;TCompactProtocol: 压缩格式;TJSONProtocol: JSON格式;TSimpleJSONProtocol: 提供只写的JSON协议;
thrift支持的Server模型
thrift主要支持以下几种服务模型
TSimpleServer: 简单的单线程服务模型,常用于测试;TThreadPoolServer: 多线程服务模型,使用标准的阻塞式IO;TNonBlockingServer: 多线程服务模型,使用非阻塞式IO(需要使用TFramedTransport数据传输方式);THsHaServer:THsHa引入了线程池去处理,其模型读写任务放到线程池去处理,Half-sync/Half-async处理模式,Half-async是在处理IO事件上(accept/read/write io),Half-sync用于handler对rpc的同步处理;
thrift IDL文件
thrift IDL不支持无符号的数据类型,因为很多编程语言中不存在无符号类型,thrift支持一下几种基本的数据类型
byte: 有符号字节i16: 16位有符号整数i32: 32位有符号整数i64: 63位有符号整数double: 64位浮点数string: 字符串
此外thrift还支持以下容器类型:
list: 一系列由T类型的数据组成的有序列表,元素可以重复;set: 一系列由T类型的数据组成的无序集合,元素不可重复;map: 一个字典结构,Key为K类型,Value为V类型,相当于java中的HashMap;
thrift容器中元素的类型可以是除了service之外的任何类型,包括exception
thirft支持struct类型,目的就是讲一些数据聚合在一起,方便传输管理,struct定义形式如下:
struct People {
1:string name;
2:i32 age;
3:string gender;
}
thrift支持枚举类型,定义形式如下:
enum Gender {
MALE,
FEMALE
}
thrift支持自定义异常类型exception,异常定义形式如下:
exception RequestException {
1:i32 code;
2:string reason;
}
thrift定义服务相当于Java中创建接口一样,创建的service经过代码生thrift代码生成工具编译后就会生成客户端和服务端的框架代码,service的定义形式如下:
service HelloWorldService {
// service中可以定义若干个服务,相当于Java Interface中定义的方法
string doAction(1:string name, 2:i32 age);
}
thrift支持给类型定义别名,如下所示:
typedef i32 int
typedef i64 long
thrift也支持常量的定义,使用const关键字:
const i32 MAX_RETRIES_TIME = 10;
const string MY_WEBSITE = "http://facebook.com";
thrift支持命名空间,命名空间相当于Java中的package,主要用于组织代码,thrift使用关键字namespace定义命名空间,格式是namespace 语言名 路径,如下示例所示:
namespace java com.test.thrift.demo
thrift也支持文件包含,相当于CPP中的include,Java中的import,使用关键字include:
include "global.thrift"
#、//、/**/都可以作为thrift文件中的注释。
thrift提供两个关键字required和optional,分别用于表示对应的字段是必填的还是可选的(推荐尽量使用optional),如下所示:
struct People {
1:required string name;
2:optional i32 age;
}
thrift应用示例
本示例中我们使用java编写thrift的服务端程序,使用python编写thrift的客户端程序。
首先定义thrift IDL文件
// 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关键字用于定义结构体,相当于面向对象编程语言中的类
struct Person {
// 相当于定义类中的成员,并生成相应的get和set方法,optional表示username这个成员可以没有
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 data),
void savePerson(1: required Person person)
}
执行thrift --gen java src/thrift/data.thrift生成对应的java代码,并引入到Java工程当中,代码结构如下图所示
编写Java服务端代码,data.thrift的service中定义了两个服务,我们需要定义相应服务的实现类(相当于handler),如下所示:
import thrift.generated.DataException;
import thrift.generated.Person;
import thrift.generated.PersonService;
public class PersonServiceImpl implements PersonService.Iface {
@Override
public Person getPersonByUsername(String username) throws DataException {
System.out.println("Got Client Param: " + username);
return new Person().setUsername(username).setAge(20).setMarried(false);
}
@Override
public void savePerson(Person person) throws DataException {
System.out.println("Got Client Param:");
System.out.println(person.username);
System.out.println(person.age);
System.out.println(person.married);
}
}
同时我们需要借助thrift为我们提供的类库实现一个服务器来监听rpc请求,代码如下所示:
import org.apache.thrift.TProcessorFactory;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.THsHaServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import thrift.generated.PersonService;
public class ThriftServer {
public static void main(String[] args) throws Exception {
// 定义服务器使用的socket类型
TNonblockingServerSocket tNonblockingServerSocket = new TNonblockingServerSocket(8899);
// 创建服务器参数
THsHaServer.Args arg = new THsHaServer.Args(tNonblockingServerSocket).minWorkerThreads(2).maxWorkerThreads(4);
// 请求处理器
PersonService.Processor<PersonServiceImpl> processor = new PersonService.Processor<>(new PersonServiceImpl());
// 配置传输数据的格式
arg.protocolFactory(new TCompactProtocol.Factory());
// 配置数据传输的方式
arg.transportFactory(new TFramedTransport.Factory());
// 配置处理器用来处理rpc请求
arg.processorFactory(new TProcessorFactory(processor));
// 本示例中使用半同步半异步方式的服务器模型
TServer server = new THsHaServer(arg);
System.out.println("Thrift Server Started!");
// 启动服务
server.serve();
}
}
编写python客户端,执行thrift --gen py src/thrift/data.thrift生成对应的python代码,并引入到python工程当中,代码结构如下图所示
python客户端代码如下所示:
# -*- coding:utf-8 -*-
__author__ = 'kpzhang'
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(900)
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)
transport.close()
except Thrift.TException, tx:
print '%s' % tx.message
客户端可以向调用本地的方法一样调用服务端的方法。
[thrift] thrift基本原理及使用的更多相关文章
- Thrift 安装及使用
前言:由于最近在看storm Topology提交过程的源代码,写好的topology jar文件是通过Thrift RPC的形式提交给nimbus的.故了解下Thrift的基本原理. 参考:http ...
- Thrift-java学习小结
➠更多技术干货请戳:听云博客 Thrift是什么?什么情况下使用thrift Thrift源于大名鼎鼎的facebook之手,在2007年facebook提交Apache基金会将Thrift作为一个开 ...
- RPC学习----Thrift快速入门和Java简单示例
一.什么是RPC? RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议. RPC协议 ...
- rpc框架之 thrift 学习 1 - 安装 及 hello world
thrift是一个facebook开源的高效RPC框架,其主要特点是跨语言及二进制高效传输(当然,除了二进制,也支持json等常用序列化机制),官网地址:http://thrift.apache.or ...
- 解决thrift: ···No such file or directory问题
感谢Anker分享:error while loading shared libraries: xxx.so.x" 错误的原因和解决办法 今天在装thrift的时候遇到一个这样的问题: ro ...
- thrift编译安装
关于thrift的介绍:http://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/ Apache Thrift 是 Facebook 实现 ...
- thrift demo
基于上一篇博客,安装thrift complier之后,就需要进行跑跑程序,来看看是否如同预期的那种效果. 前面的thrift compiler的主要作用,其实就是为了IDL的,就是防止客户端和服务端 ...
- 安装thrift全过程
为了研究基于thrift的RPC框架,其实,是想自己基于thrift写一个微服务的platform.首先就是安装Thrift,便于IDL架构生成java的接口文件.多的不说了,开始install的过程 ...
- Apache Thrift学习之一(入门及Java实例演示)
目录: 概述 下载配置 基本概念 数据类型 服务端编码基本步骤 客户端编码基本步骤 数据传输协议 实例演示(java) thrift生成代码 实现接口Iface TSimpleServer服务模型 T ...
随机推荐
- Polynomial Division 数学题
https://www.hackerrank.com/contests/101hack45/challenges/polynomial-division 询问一个多项式能否整除一个一次函数.a * x ...
- B. Hongcow Solves A Puzzle
http://codeforces.com/contest/745/problem/B 题目要求的是,给定一个图形,要求里面判断是否有矩形,且仅有一个 就是 XXX.... XXX...X 是不行的, ...
- sdut2784&cf 126b Good Luck!(next数组)
链接 next数组的巧妙应用 学弟出给学弟的学弟的题.. 求最长的 是前缀也是后缀同时也是中缀的串 next的数组求的就是最长的前后缀 但是却不能求得中缀 所以这里 就把尾部去掉之后再求 这样就可 ...
- Android开发学习——简单类图
1.类A继承于类B (B 是父类,A是 子类) 2.小汽车.自行车 实现车的接口 3.A中有B这个成员变量,单向关联 4.聚合,整体与部分的关系.has-a B中的构造函数(或set方法)需要 ...
- 浏览器和ES5的介绍
浏览器的组成 : shell(浏览器的外壳).内核(渲染引擎.js引擎)主流浏览器及其内核: IE tritent(IE9及以下,IE10及以上用webkit) ...
- (2) Tomcat启动Jenkins
Tomcat启动Jenkins 1. 下载Tomact,解压缩到指定目录. 2. 下载Jenkins.war文件,方到Tomact的WebApps下面即可. 3. 修改Tomcat的HTTP端口和默认 ...
- 阿里云服务器安装ss使用
下载安装服务器版shadowsocks yum install epel-release yum update yum install python-setuptools m2crypto super ...
- html5开发移动混合App系列2-开发环境搭建(windows)
Java下载: http://www.oracle.com/technetwork/java/javase/downloads/index.html 环境变量: JAVA_HOME=C:\Progra ...
- cookie设置和读取以及获取超链接参数
function setCookie(c_name, value, expiredays) { var exdate = new Date() exdate.setDate(exdate.getDat ...
- 迅为4412嵌入式安卓开发板兼容3G网络|4G网络
iTOP-Exynos4412开发板内置有无线 WIFI 模块.Bluetooth.GPS.Camera.3G等模组,陀螺仪等,支持 HDMI1.4(1080P/60Hz)显示,客户可以直接从开发平台 ...