公司的一些平台服务框架底层封装了thrift提供服务,最近项目不是很紧,于是研究了一下,刚刚入门,理解得不深,写这篇博文来整理一下思路.
什么是thrift?
简单来说,是Facebook公布的一款开源跨语言的RPC框架.
那么问题来了.
什么是RPC框架?
RPC全称为Remote Procedure Call,意为远程过程调用.
假设有两台服务器A,B.A服务器上部署着一个应用a,B服务器上部署着一个应用b,现在a希望能够调用b应用的某个函数(方法),但是二者不在同一个进程内,不能直接调用,就需要通过网络传输,在AB服务器之间建一条网络传输通道,a把参数传过去,b接收到参数调用自己的方法,得到结果,再通过网络传回给a,简单讲就是A通过网络来调用B的过程.这个过程要涉及的东西很多,比如多线程,Socket,序列化反序列化,网络I/O,很复杂,于是牛掰的程序员把这些封装起来做成一套框架,供大家使用,就是RPC框架.
thrift的跨语言特型
thrift通过一个中间语言IDL(接口定义语言)来定义RPC的数据类型和接口,这些内容写在以.thrift结尾的文件中,然后通过特殊的编译器来生成不同语言的代码,以满足不同需要的开发者,比如java开发者,就可以生成java代码,c++开发者可以生成c++代码,生成的代码中不但包含目标语言的接口定义,方法,数据类型,还包含有RPC协议层和传输层的实现代码.
thrift的协议栈结构
thrift是一种c/s的架构体系.在最上层是用户自行实现的业务逻辑代码.第二层是由thrift编译器自动生成的代码,主要用于结构化数据的解析,发送和接收。TServer主要任务是高效的接受客户端请求,并将请求转发给Processor处理。Processor负责对客户端的请求做出响应,包括RPC请求转发,调用参数解析和用户逻辑调用,返回值写回等处理。从TProtocol以下部分是thirft的传输协议和底层I/O通信。TProtocol是用于数据类型解析的,将结构化数据转化为字节流给TTransport进行传输。TTransport是与底层数据传输密切相关的传输层,负责以字节流方式接收和发送消息体,不关注是什么数据类型。底层IO负责实际的数据传输,包括socket、文件和压缩数据流等。
MAC OS下thrift的下载与安装
我的电脑是mac,第一次安装也碰到了一些问题,所以有必要记录一下.
首先,在官网下载安装包http://thrift.apache.org/download 目前官网是0.10.0版本.下载完之后解压到想要安装的目录.
进入根目录:
step1:cd thrift-0.10.0 step2:./configure step3:make step4:make install
安装的时候,第二步出现了问题,提示:
Bison version 2.5 or higher must be installed on the system!
原因是Bison版本过低,mac默认安装的版本是2.3,因此需要安装最新版的Bison,命令行输入:brew install bison安装最新版bison,如果你的命令行反馈:Command not found,很可能是因为你没装homebrew,命令行输入:
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
等待安装完homebrew就可以安装bison了,最新版本3.0.4安装完以后,执行第二步,发现依然提示上面那个警告,原因是因为它读取的仍然是默认的bison,于是找到系统安装的bison目录,我的mac是
/Library/Developer/CommandLineTools/usr/bin/bison,解决方法也比较简单,可以先将这个目录下的bison名字改一下,再将最新版的bison复制进来,于是,在bin目录下,执行命令:
sudo mv bison bison111
sudo cp /usr/local/opt/bison/bin/bison /Library/Developer/CommandLineTools/usr/bin/
现在,再按照上面的步骤进行下去,就可以正确安装thrift了.
进入thrift大门的第一个java小实例
1,创建一个服务Hello,创建文件Hello.thrift,代码如下:
namespace java service.demo service Hello{ string helloString(1:string para) }
这里定义了一个名为helloString的方法,入参和返回值都是一个string类型的参数.
2,终端进入Hello.thrift所在目录,执行命令:
thrift -r -gen java Hello.thrift
发现在当前目录下多了一个gen-java的目录,里面的有一个Hello.java的文件.这个java文件包含Hello服务的接口定义Hello.Iface,以及服务调用的底层通信细节,包括客户端的调用逻辑Hello.Client以及服务端的处理逻辑Hello.Processor,
3,创建一个Maven管理的Java项目,pom.xml中添加相关的依赖,并将Hello.java文件复制到项目中:
<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>
4,创建HelloServiceImpl实现Hello.Iface接口:
package service.demo; import org.apache.thrift.TException; /** * @author yogo.wang * @date 2017/02/21-下午2:13. */ public class HelloServiceImpl implements Hello.Iface { public String helloString(String para) throws TException { return "result:"+para; } }
5,创建服务端实现代码HelloServiceServer,把HelloServiceImpl作为一个具体的处理器传递给Thrift服务器:
package service.demo; 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; /** * @author yogo.wang * @date 2017/02/21-下午2:15. */ public class HelloServiceServer { /** * 启动thrift服务器 * @param args */ public static void main(String[] args) { try { System.out.println("服务端开启...."); TProcessor tprocessor = new Hello.Processor<Hello.Iface>(new HelloServiceImpl()); // 简单的单线程服务模型 TServerSocket serverTransport = new TServerSocket(9898); TServer.Args tArgs = new TServer.Args(serverTransport); tArgs.processor(tprocessor); tArgs.protocolFactory(new TBinaryProtocol.Factory()); TServer server = new TSimpleServer(tArgs); server.serve(); }catch (TTransportException e) { e.printStackTrace(); } } }
6,创建客户端实现代码HelloServiceClient,调用Hello.client访问服务端的逻辑实现:
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; import org.apache.thrift.transport.TTransportException; /** * @author yogo.wang * @date 2017/02/21-下午2:35. */ public class HelloServiceClient { public static void main(String[] args) { System.out.println("客户端启动...."); TTransport transport = null; try { transport = new TSocket("localhost", 9898, 30000); // 协议要和服务端一致 TProtocol protocol = new TBinaryProtocol(transport); Hello.Client client = new Hello.Client(protocol); transport.open(); String result = client.helloString("哈哈"); System.out.println(result); } catch (TTransportException e) { e.printStackTrace(); } catch (TException e) { e.printStackTrace(); } finally { if (null != transport) { transport.close(); } } } }
全部工作完成后,下面来测试一下,先执行服务端main方法,在执行客户端main方法,会在客户端控制台打印出:result:哈哈.