thrift

背景

先来想这么个问题:有个订单服务order部署在A服务器,还有个用户服务user部署在B服务器,现在前端点击下单,在调用A服务器的order服务时,同时order服务器要判断用户信息是否正确。

这时候你可以找到前端小伙伴,告诉他能不能在点下单时先调用B服务器的user服务,然后在返回信息之后调用A服务器的order,前端小伙伴肯定会给你个鄙视的眼神~

现在是在不同的服务器,就像隔河相望,而就算想发消息出去,对方又不能接收。必须得先让B服务器的user服务提供一个接口,才能够外部访问啊。。。

我们今天讲的thrift,就可以很好的解决这个问题~

在讲thrift之前我们不得不先说下RPC

RPC (Remote Procedure Call)

RPC中文翻译过来叫远程过程调用,是计算机的一种通信的协议,该协议允许在不同服务器的进程相互调用。也就是说通过RPC技术,虽然进程不在同一台服务器,但是我可以像调用自身服务一样调用其他服务器上的服务,它会帮我们封装网络编程的细节(网络通信是很复杂的),让我们将更多的精力投入到业务本身上边。
RPC是典型的C/S架构,还记得java的网络编程吗?服务端需要启动ServerSocket对象,调用bind()绑定监听端口,lisener()设置监听队列,然后调用accept方法接收请求。客户端开启一个socket,调用connect()去连接一个serverSocket,调用send()发送数据:

javasocket

RPC调用过程

执行一次RPC操作则是需要做以下几个操作的(图摘网络):


rcpcall
  1. 客户端调用程序
  2. 客户端桩(stub)构建要发送的信息
  3. 该信息通过网络被发送到服务器端
  4. 服务器端的操作系统接收到消息,将其交给服务端桩(stub)
  5. 服务器端桩(stub)解开包,拿到消息内容
  6. 服务器桩(stub)调用本地的方法

要实现一个完整的RPC是很复杂的,现在网上也有开源可用的RPC工具,它们使用IDL(interface description language)接口定义语言来提供跨平台跨语言的服务调用,thrift就是其中一种,它适用于多种场景,支持不同的语言,在跨语言时保持性能和易用性。

Thrift

thrift是一个跨平台,跨语言的RPC工具,是由facebook开源的一个项目。thrift通过IDL(接口定义语言)来定义RPC的接口和数据类型,然后使用编译器编译成不同语言的接口文件,这个生成的接口文件已经负责了RPC通信的实现了。

我们先想下,面向接口编程中,客户端不清楚服务端是如何实现的,当客户端调用接口方法时,是有具体的实现类去完成功能。我们在此,先将thrift接口信息分别放入客户端和服务端,令服务端实现这个接口,客户端调用这个接口,而这个接口是由thrift生成的,已经封装好了通信协议,那么即使客户端和服务端部署在不同的服务器,也可以正常调用,发送请求。

Demo:

创建thrift接口

说多无用,来个代码(talk is cheap, show me the code)
在写代码之前需要在电脑中安装thrift编译器(这个自行网上搜索即可),安装完后创建一个thrift文件:HiThrift.thrft,内容如下:

namespace java com.lsd.service
service HiThrift{
    string sayHello(1:string name)
}

java后边的是包名,创建完成后用thrift命令编译此信息:


thrift

这个是生成了java的文件,当然也可以生成其他语言的接口。
会发现在同路径下多了gen-java目录,在目录最后有个HiThrift.java文件,这个文件就是thrift生成的,打开后发现好多内容~

服务端

创建maven项目,引入thrift依赖:

<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>

版本自己选择即可。
接下来将生成的HiThrift.java放入工程中(注意包路径对应好),然后创建其实现类

public class ThriftServer {

    public static void main(String[] args) {
        try {
            TProcessor processor = new HiThrift.Processor<HiThrift.Iface>(new HiThriftImpl());
            TServerSocket tServerSocket = new TServerSocket(8888);
            TBinaryProtocol.Factory factory = new TBinaryProtocol.Factory();
            TServer.Args tArgs = new TServer.Args(tServerSocket);
            tArgs.processor(processor);
            tArgs.protocolFactory(factory);
            TServer tServer = new TSimpleServer(tArgs);
            tServer.serve();
            System.out.println("服务端启动成功~");
        } catch (TTransportException e) {
            System.out.println(e);
        }
    }
}

在TServerSocket中我们写了8888,这个是服务启动监听的端口号,之后我们会讲解TProcessor,TBinaryProtocol,TSimpleServer等是啥东东~
右键运行main方法,此程序运行,它现在已经在监听本地的8888端口。

客户端

在本服务中建立一个服务调用方:

public class ThriftClient {
    public static void main(String[] args) {
        try (TTransport transport = new TSocket("localhost", 8888, 30000)) {
            TProtocol protocol = new TBinaryProtocol(transport);
            HiThrift.Client client = new HiThrift.Client(protocol);
            transport.open();
            String result = client.sayHello("张三");
            System.out.println(result);
        } catch (final Exception e) {
            e.printStackTrace();
        }
    }
}

我们创建了一个TSocket,目的是向localhost的8888端口发送请求,超时时间设定30000ms。

现在就完成了客户端和服务端的编码工作,我们运行下客户端的main()启动程序:


diaoyongjieguo

发现在客户端已经输出了结果~

是不是很神奇,客户端没有依赖任何服务实现类,服务端却执行了代码。

Thrift协议

上边我们用一个简单的例子使用了thrift完成了一次RPC调用,但是公司里肯定不会这么用,你想想谁会把ip啥的写到代码里,一般会把ip等信息存放到一个公共的地方,让服务端启动的时候存进去,客户端想调用的时候拿出来,这个地方就叫做服务中心,现在有很多的工具都可以完成项任务,比如说eureka,zookeeper,nacos,redis等等,这服务中心会在另一篇文章中具体介绍,此处不再赘述~

首先看下thrift协议栈(图片来源网络):

thriftxieyi
  • 底层IO,负责数据的网络传输,Socket,File,Zip
  • TTansport是以字节流(Byte Stream)方式发送和接收信息,每一个底层IO模块都会有一个对应的TTransport来负责字节流在该IO模块的传输。比如TSocket对应Socket传输,TFileTransport对应文件传输。
  • TProtocol是将数据组装成信息,或者将信息读取出来形成数据,即将发送的数据转换成字节流(Data Stream)和将字节流转换成数据(也就是将字节流传给TTransport或者从TTransport中读取成字节数据)
  • TServer:接收Client的请求,并将请求转发到Processor进行处理
  • TProcessor:对Client的请求做出响应,包括RPC请求转发,调用参数解析和代码逻辑调用,返回值写回等

TTransport

TTransport下边就是底层IO了,所以TTransport要支持底层IO。到了TTransport这一层,数据就是按照字节流(Byte Stream)处理,并不关心数据到底是什么类型,什么内容。
TTransport分为以下几类:

  • TSocket:阻塞的TCP Socket进行数据传输
  • TServerTransport:服务端接收传输对象

TProtocol

它是把TTransport传过来的字节流(Byte Stream)转换为数据流(Date Stream),它对数据来说就像是一个分水岭,从这个协议往上,数据就是数据,有自己的类型,名称等,从这个协议往底层走,就变成了字节流传输,没有任何的意义了。
在这里用户可以自选协议:

  • TBinaryProtocol:二进制格式 (这个用的多)
  • **TCompactProtocol **:压缩格式 (这个效率高)
  • TJSONProtocol :使用JSON的数据编码协议进行数据传输
  • TSimpleJSONProtocol:提供JSON只写协议,生成的文件很容易通过脚本语言解析
  • TDebugProtocol:使用易懂的可读的文本格式,以便于debug

TServer

上边讲了TServer是用来处理客户端请求,将请求转发给Processor的。所以TServer的好坏影响着整个流程的性能。thrift对TServer有不同的实现,来解决访问量或多或少的情况:

  • TSimpleServer:上述demo中用到的,使用BIO单线程服务器
  • TThreadPoolServer:BIO多线程服务器
  • TNonBlockingServer:NIO单线程服务器,用少量线程就可以完成高并发请求(必须使用TFramedTransport)
  • THsHaServer:它是TNonBlockingServer的子类,它觉得父类是单线程处理,效率不高,所以自己搞了个线程池处理业务
  • TThreadedSelectorServer:这个实现较为复杂,也是TServer里最高级的,它维护了两个线程池,一个用来处理网络I/O,另一个用ExecutorService来进行业务的处理
    推荐一篇文章thrift各种TServer实现

TProcessor

走到这一步,相当于数据从thrift框架中拿到了,要调用真正的接口实现类了。
它有个process函数,任何调用都会经过此方法,然后转向特定的地方。

它会解析请求数据(反序列化),解析参数信息,然后调用实现类方法,完成真正的请求,最后将返回值进行包装(序列化),传给TProtocol。

它的逻辑有点像springmvc(自我感觉哈),你看springmvc,不就是所有请求走dipatcherServlet吗,任何请求被它拦截之后,都会调用dispatch()->doDispatch()...

总结:

thrift是一个RPC工具,它封装了网络传输的细节,让我们集中心思到业务逻辑上。让我们可以像调用本地代码一样调用远程服务

thrift内部结构非常清晰,从底层IO一直到最上层的TProcessor,各个层级负责各自的事情,高效

thrift可以让用户根据不同的场景定制不同的处理策略并发量小可以选择TSimpleServer或者TThreadPoolServer,并发量太大则可以选择THsHaServer或者TThreadedSelectorServer,增加了框架的灵活性

还有重要的一点,thrift是跨语言的,不管是java,php,python...都可以实现自己的服务,或者调用不同语言的服务~

到此,我们的thrift解析就over啦,说下开篇的问题,我们可以生成一个thrift接口,在B服务器中添加该接口并在user服务中实现它(当然关于IP和端口这个不可能固定到代码中,它是由服务中心去处理),然后在A服务器的order服务中引入该接口,在调用下单逻辑之前,通过thrift请求到B服务器的user服务,这时就可以正常访问啦~

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

推荐阅读更多精彩内容