Thrift入门

原文链接:thrift入门 转载请注明出处~

Thrift简介

什么是thrift

简单来说,是Facebook公布的一款开源跨语言的RPC框架.

什么是RPC框架?

RPC (Remote Procedure Call Protocal),远程过程调用协议


rpc

RPC, 远程过程调用直观说法就是A通过网络调用B的过程方法。

  • 简单的说,RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。

  • RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯) RPC 是一个请求响应模型。

  • 客户端发起请求,服务器返回响应(类似于Http的工作方式) RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。

早期单机时代,一台电脑上运行多个进程,大家各干各的,老死不相往来。假如A进程需要一个画图的功能,B进程也需要一个画图的功能,程序员就必须为两个进程都写一个画图的功能。这不是整人么?于是就出现了IPC(Inter-process communication,单机中运行的进程之间的相互通信)。OK,现在A既然有了画图的功能,B就调用A进程上的画图功能好了,程序员终于可以偷下懒了。
到了网络时代,大家的电脑都连起来了。以前程序只能调用自己电脑上的进程,能不能调用其他机器上的进程呢?于是就程序员就把IPC扩展到网络上,这就是RPC(远程过程调用)了。现在不仅单机上的进程可以相互通信,多机器中的进程也可以相互通信了。要知道实现RPC很麻烦呀,什么多线程、什么Socket、什么I/O,都是让咱们普通程序员很头疼的事情。于是就有牛人开发出RPC框架(比如,CORBA、RMI、Web Services、RESTful Web Services等等)。OK,现在可以定义RPC框架的概念了。简单点讲,RPC框架就是可以让程序员来调用远程进程上的代码一套工具。有了RPC框架,咱程序员就轻松很多了,终于可以逃离多线程、Socket、I/O的苦海了。

thrift的跨语言特型

thrift通过一个中间语言IDL(接口定义语言)来定义RPC的数据类型和接口,这些内容写在以.thrift结尾的文件中,然后通过特殊的编译器来生成不同语言的代码,以满足不同需要的开发者,比如java开发者,就可以生成java代码,c++开发者可以生成c++代码,生成的代码中不但包含目标语言的接口定义,方法,数据类型,还包含有RPC协议层和传输层的实现代码.

thrift的协议栈结构
thrift协议栈结构

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

Thrift安装

安装环境:window 7

  • 官网上下载thrift-0.9.3.exe包到一个新建文件夹(博主的文件夹名称为Thrift)中
  • 然后将此文件夹放到环境变量Path中。例如博主就是将D:Thrift添加到Path中
  • cmd,打开终端,输入thrift -version,即可看到相应的版本号,就算是成功安装啦

ThriftDemo

下面,来做个小Demo来熟悉Thrift的使用流程

  1. 首先在一个目录下,创建一个文件,博主是用NotePad++创建的,用windows自带的记事本貌似也是可以的,这里创建了一个thrift脚本,命名为login.thrift,内容如下
namespace java com.game.lll.thrift  
  
struct Request {  
    1: string username;        
    2: string password;             
}  
  
exception RequestException {  
    1: required i32 code;  
    2: optional string reason;  
}  
  
// 服务名  
service LoginService {  
    string doAction(1: Request request) throws (1:RequestException qe); // 可能抛出异常。  
}  
  1. 终端进入当前文件夹,在终端输入命令thrift -gen java login.thrift。当前目录下会生成一个gen-java文件夹,文件夹下会按照namespace定义的路径名一层层生成文件夹,到最里层的文件夹里可以看到生成的3个java类Request.java,RequestException.java,LoginService.java
  1. 用IDEA/Eclipse新建一个工程,并为此工程添加依赖。博主创建的是Maven工程,就在pom.xml里面添加依赖,如下
  <!-- https://mvnrepository.com/artifact/org.apache.thrift/libthrift -->
    <dependency>
        <groupId>org.apache.thrift</groupId>
        <artifactId>libthrift</artifactId>
        <version>0.9.3</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.21</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-nop -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-nop</artifactId>
        <version>1.7.21</version>
    </dependency>
  1. 将目录中的代码拷贝到工程的classpath下,IDEA中的classpath就是source所处的文件夹(会有颜色标识,博主的版本是蓝色文件夹)
  2. 创建LoginServiceImpl.java类,实现在LoginService.Iface接口
/**
 * Created by CiCi on 2017/5/16.
 */
import org.apache.thrift.TException;

import com.game.lll.thrift.LoginService;
import com.game.lll.thrift.Request;
import com.game.lll.thrift.RequestException;

public class LoginServiceImpl implements LoginService.Iface{

    @Override
    public String doAction(Request request) throws RequestException,TException {
        // TODO Auto-generated method stub
        System.out.println("hahaha");
        System.out.println("username:"+request.getUsername());
        System.out.println("password:"+request.getPassword());
        return request.getUsername()+request.getPassword();
    }

}
  1. 新建服务器端LoginMain.java
/**
 * Created by CiCi on 2017/5/16.
 */
import java.net.ServerSocket;

import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;

import com.game.lll.thrift.LoginService;
import com.game.lll.thrift.LoginService.Processor;

public class LoginMain {
    public static void main(String[] args) throws Exception {
        // Transport
        ServerSocket socket = new ServerSocket(8888);
        TServerSocket serverTransport = new TServerSocket(socket);

        // Processor
        LoginService.Processor processor = new Processor(new LoginServiceImpl());

        TServer.Args tServerArgs = new TServer.Args(serverTransport);
        tServerArgs.processor(processor);

        // Server
        TServer server = new TSimpleServer(tServerArgs);
        System.out.println("Starting the simple server...");
        server.serve();
    }
}
  1. 新建客户端
/**
 * Created by CiCi on 2017/5/16.
 */
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;

import com.game.lll.thrift.LoginService;
import com.game.lll.thrift.Request;

public class ClientMain {
    public static void main(String[] args) throws Exception {
        TTransport transport = null;
        try {
            // 创建TTransport
            transport = new TSocket("localhost", 8888);

            // 创建TProtocol 协议要与服务端一致
            TProtocol protocol = new TBinaryProtocol(transport);

            // 创建client
            LoginService.Client client = new LoginService.Client(protocol);

            transport.open();  // 建立连接
            
            Request request = new Request().setUsername("liulongling").setPassword("123456");
             
            // client调用server端方法
            System.out.println(client.doAction(request));
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            transport.close();  // 请求结束,断开连接

        }

    }
}


  1. 运行服务器端,控制台输出结果“Starting the simple server...”
  2. 运行客户端,控制台输出结果
    username:lalala
    password:123456

Thrift使用流程

服务端编码的基本流程
  1. 创建TTransport
  2. 创建TProtocol
  3. 创建TProcessor
  4. 创建Server
  5. 启动服务
客户端编码的基本流程
  1. 创建TTransport
  2. 创建TProtocol
  3. 创建Client
  4. client方法调用
Transport

Transport层提供了一个简单的网络读写抽象层,这使得thrift底层的transport从系统其他的部分解耦。Thrift使用ServerTransport接口接受或者创建原始transport对象。ServerTransport用在Server端,为到来的连接创建Transport对象。

Protocol

Protocol抽象层定义了一种怎样将内存中数据结构映射成可传输格式的机制。Protocol定义了datatype怎样使用底层的Transport对自己进行编解码

Processor

Processor封装了从输入数据流中读数据和向数据流中写数据的操作。

interface TProcessor {
 
bool process(TProtocol in, TProtocol out) throws TException
 
}

与服务相关的processor实现由编译器产生。Processor主要工作流程如下:从连接中读取数据(使用输入protocol),将处理授权给handler(由用户实现),最后将结果写到连接上(使用输出protocol)。

Server

Server将以上所有特性集成在一起

  • 创建一个transport对象
  • 为transport对象创建输入输出protocol
  • 基于输入输出protocol创建processor
  • 等待连接请求并将之交给processor处理

Thrift语法

基本类型

thrift不支持无符号类型,因为很多编程语言不存在无符号类型

  • byte:有符号字节
  • i16:16位有符号整数
  • i32:32位有符号整数
  • i64:64位有符号整数
  • double:64位浮点数
  • string:字符串类型
容器类型

集合黄总的元素可以是除了service之外的任何类型

  • list<<T>T>:一系列由T类型的数据组成的有序列表,元素可以重复
  • set<<T>T>:一系列由T类型的数据组成的无序集合,元素不可重复
  • map<K, V>:一个字典结构,key为K类型,value为V类型
其他类型
结构体(struct)

thrift支持struct类型,目的是将一些数据聚合在一起,方便传输管理,struct定义如下

struct People {
     1: string name;
     2: i32 age;
     3: string sex;
}
枚举(enum)

枚举的定义形式和Java的Enum类似,例如:

enum Sex {
    RED,
    BLUE
}
异常(exception)

thrift支持自定义异常

exception RequestException {
     1: i32 code;
    2: string reason;
}
服务(Service)

thrift定义的服务相当于Java中创建Interface一样,创建的Service经过代码生成命令后会生成客户端与服务端的框架代码,定义形式如下

service HelloWordService {
     // service中定义的函数,相当于Java interface中定义的函数
     string doAction(1: string name, 2: i32 age);
 }
类型定义

thrift支持类似C++一样的typedef 定义,注意末尾没有逗号或者分号,比如

typedef i32 Integer
typedef i64 Long
常量(const)

thrift使用const关键字定义常量,末尾的分号是可选的,可有可无

const i32 MAX_RETRIES_TIME = 10
命名空间(namespace)

thrift的命名空间相当于Java中的package,主要目的是组织代码。格式为

namespace <语言> <包的位置>
eg:namespace java.com.test.thrift
文件包含

thrift支持文件包含,相当于C/C++中的include,使用关键字include定义

include "global.thrift"
注释

thrift注释方式支持shell风格的注释,支持C/C++风格的注释,即#和//开头的语句都单当做注释,/**/包裹的语句也是注释。

可选与必选

thrift提供两个关键字required,optional,分别用于表示对应的字段时必填的还是可选的。例如:

struct People {
    1: required string name;
    2: optional i32 age;
}

表示name是必填的,age是可选的。

参考文献

thrift入门教程
Thrift入门初探--thrift安装及java入门实例
Thrift
Thrift RPC实战(一) 初次体验Thrift
【Apache Thrift】windows下thrift的安装(一)
【Apache Thrift】Thrift的使用和编译(二)
Thrift入门初探(2)--thrift基础知识详解
Thrift使用指南
Thrift入门及Java实例演示

个人博客地址:kongdehui.com 欢迎批评指正~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容