thrift使用


一、thrift介绍

1.Thrift概述   

Thrift是一个跨语言的服务部署框架,最初由Facebook开发用做系统内部多种语言之间的RPC通信,07年四月开放源码,08年5月进入apache孵化器。Thrift通过一个中间语言(IDL, 接口定义语言)来定义RPC的接口和数据类型,然后通过一个编译器生成PC客户端和服务器通信的不同语言的代码(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml),并由生成的代码负责RPC协议层和传输层的实现。

2.Thrift架构介绍:


Thrift实际上是实现了C/S模式,通过代码生成工具将接口定义文件生成服务器端和客户端代码(可以为不同语言),从而实现服务端和客户端跨语言的支持。用户在Thirft描述文件中声明自己的服务,这些服务经过编译后会生成相应语言的代码文件,然后用户实现服务(客户端调用服务,服务器端提服务)便可以了。其中protocol(协议层, 定义数据传输格式,可以为二进制或者XML等)和transport(传输层,定义数据传输方式,可以为TCP/IP传输,内存共享或者文件共享等)被用作运行时库。

3、 thrift支持的数据传输格式、数据传输方式和服务模型
(1)支持的传输格式
TBinaryProtocol – 二进制格式.
TCompactProtocol – 压缩格式
TJSONProtocol – JSON格式
TSimpleJSONProtocol –提供JSON只写协议, 生成的文件很容易通过脚本语言解析。
TDebugProtocol – 使用易懂的可读的文本格式,以便于debug
(2) 支持的数据传输方式
TSocket -阻塞式socket
TFramedTransport – 以frame为单位进行传输,非阻塞式服务中使用。
TFileTransport – 以文件形式进行传输。
TMemoryTransport – 将内存用于I/O. java实现时内部实际使用了简单的ByteArrayOutputStream。
TZlibTransport – 使用zlib进行压缩, 与其他传输方式联合使用。当前无java实现。
(3)支持的服务模型
TSimpleServer – 简单的单线程服务模型,常用于测试
TThreadPoolServer – 多线程服务模型,使用标准的阻塞式IO。
TNonblockingServer – 多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式)

处理大量更新的话,主要是在TThreadedServer和TNonblockingServer中进行选择。TNonblockingServer能够使用少量线程处理大量并发连接,但是延迟较高;TThreadedServer的延迟较低。实际中,TThreadedServer的吞吐量可能会比TNonblockingServer高,但是TThreadedServer的CPU占用要比TNonblockingServer高很多。

4.Thrift数据类型:

Thrift类型系统包括预定义基本类型,用户自定义结构体,容器类型,异常和服务定义。


(1)基本类型:
bool:布尔值,true 或 false,对应 Java 的 boolean
byte:8 位有符号整数,对应 Java 的 byte
i16:16 位有符号整数,对应 Java 的 short
i32:32 位有符号整数,对应 Java 的 int
i64:64 位有符号整数,对应 Java 的 long
double:64 位浮点数,对应 Java 的 double
string:utf-8编码的字符串,对应 Java 的 String

注意,thrift不支持无符号整型,因为很多目标语言不存在无符号整型(如java)。

(2)容器类型:

Thrift容器与类型密切相关,它与当前流行编程语言提供的容器类型相对应,采用java泛型风格表示的。Thrift提供了3种容器类型:

List<t1>:一系列t1类型的元素组成的有序表,元素可以重复,对应 Java 的 ArrayList
Set<t1>:一系列t1类型的元素组成的无序表,元素唯一,对应 Java 的 HashSet
Map<t1,t2>:key/value对(key的类型是t1且key唯一,value类型是t2),对应 Java 的 HashMap
容器中的元素类型可以是除了service意外的任何合法thrift类型(包括结构体和异常)。

(3)结构体类型:
struct:定义公共的对象,类似于 C 语言中的结构体定义,在 Java 中是一个 JavaBean

Thrift结构体在概念上同C语言结构体类型—-一种将相关属性聚集(封装)在一起的方式。在面向对象语言中,thrift结构体被转换成类。

(4)异常类型:

exception:对应 Java 的 Exception

异常在语法和功能上类似于结构体,只不过异常使用关键字exception而不是struct关键字声明。但它在语义上不同于结构体—当定义一个RPC服务时,开发者可能需要声明一个远程方法抛出一个异常。
(5)服务类型:
service:对应服务的类

4.服务端编码基本步骤:
实现服务处理接口impl
创建TProcessor
创建TServerTransport
创建TProtocol
创建TServer
启动Server
5.客户端编码基本步骤:
创建Transport
创建TProtocol
基于TTransport和TProtocol创建 Client
调用Client的相应方法


二、thrift示例演示:

1.新建一个工程,结构如下:


其中,utils包用来存放通过调用thrift-0.9.0.exe程序(windows)或者shell脚本(linux)来生成thrift的RPC代码。当然,你可以直接在console界面通过命令来生成代码,我只是把整个过程整合在一个工程里,方便管理。读者可以根据自己的需要加入如下两个类中的一个。本例中在windows下演示。

(1).GenerateClass_Linux.java:

public class GenerateClass_Linux {

	/**
	 * Linux版本
	 * 执行thrift命令生成java RPC代码
	 */
	public static void main(String[] args) throws Exception {
	    String thriftFileName = "helloWorld.thrift";
		String strCmd = "thrift -r -gen java conf/" + thriftFileName;
		Runtime.getRuntime().exec("cmd /c " + strCmd).waitFor();//通过执行cmd命令调用thrift脚本程序
	}
}
(2).GenerateClass_Windows.java:
public class GenerateClass_Windows {
	/**
	 * Windows版本
	 * 调用thrift-0.9.0.exe生成java RPC代码
	 * */
	public static void main(String[] args) throws Exception {
		String thriftFileName = "helloWorld.thrift";
		String strCmd = "thrift-0.9.0.exe -r -gen java conf/" + thriftFileName;
		Runtime.getRuntime().exec("cmd /c " + strCmd).waitFor();//通过执行cmd命令调用protoc.exe程序
	}
}


2.编写thrift中间语言(即IDL(接口定义语言),也就是****.thrift文件):

namespace java org.zhu.thrift.java #注释:定义生成代码的命名空间,与你需要定义的package相对应。

service helloWorldService{ #注释:代码生成的类名,你的业务逻辑代码需要实现代码生成的ThriftCase.Iface接口
	string sayHelloWorld(1:string name)#注释 :参数类型
}

3.运行GenerateClass_Windows.java类(本例在windwos平台运行,如果是在Linux下运行,则运行GenerateClass_Linux),生成java RPC代码。

运行后,刷新testThrift工程,这时,可以看到生成了如下文件夹:


由于生产的是一个文件夹,在eclipse中被当作一个普通文件夹来对待,因此,需要把该文件夹转化为包,操作步骤为:右击gen-java文件夹,选择Build Path中的"Use as a Folder"选项 ,这是gen-java文件夹已经变成了包,如下图:

 


4.在demo包中别写如下java类:


(1).在demo包中编写刚生成的HelloWoldService.Iface接口的实现类(HelloWorldServiceImpl.java),代码如下:

package org.zhu.thrift.demo;

import org.apache.thrift.TException;
import org.zhu.thrift.gen.*;

public class HelloWorldServiceImpl implements HelloWorldService.Iface{

	@Override
	public String sayHelloWorld(String name) throws TException {
		return "Hi," + name + " ,how are you?";
	}
}


(2).编写服务端程序HelloServer.java:

package org.zhu.thrift.demo;

import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TBinaryProtocol.Factory;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.server.TThreadPoolServer.Args;
import org.apache.thrift.transport.TServerSocket;
import org.zhu.thrift.gen.HelloWorldService;
import org.zhu.thrift.gen.HelloWorldService.Processor;

public class HelloServer {
	public static final int SERVER_PORT = 8090;

	@SuppressWarnings("unchecked")
	public void startServer() {
		try {
			System.out.println("Server start");
			TServerSocket serverTransport = new TServerSocket(SERVER_PORT);  
            HelloWorldService.Processor process = new Processor(new HelloWorldServiceImpl());  
            Factory portFactory = new TBinaryProtocol.Factory(true, true);  
            Args args = new Args(serverTransport);  
            args.processor(process);  
            args.protocolFactory(portFactory);  
            TServer server = new TThreadPoolServer(args);  
            server.serve();  
		} catch (Exception e) {
			System.out.println("Server start error!!!");
			e.printStackTrace();
		}
	}
	
	 public static void main(String[] args) {  
		 HelloServer server = new HelloServer();  
	        server.startServer();  
	    }  
	
}
(3).编写客户端程序HelloClient.java:

package org.zhu.thrift.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;
import org.zhu.thrift.gen.HelloWorldService;

public class HelloClient {

	public static final int SERVER_PORT = 8090;
	public void startClient() {
		try {
			TTransport transport = new TSocket("localhost", SERVER_PORT);
			TProtocol protocol = new TBinaryProtocol(transport);
			HelloWorldService.Client client = new HelloWorldService.Client(protocol);
			transport.open();
			System.out.println(client.sayHelloWorld("zhuxun"));
			transport.close();
		} catch (TTransportException e) {
			e.printStackTrace();
		} catch (TException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		HelloClient client = new HelloClient();
		client.startClient();
	}

}
5.分别运行服务器程序HelloServer和客户端程序HelloServer.java







需要注意的问题
1. Thrift生成的server端是thread safe的. 但是client端不是thread safe. 所以需要多个thread和server端通信,则每个thread需要initiate一个自己的client实例.
2. 如果服务器采用TNonblockingServer的话,客户端必须采用TFramedTransport。程序链接的时候需要thriftnb。
3. 默认TServerSocket和TSocket都设置了NoDelay为1,使得报文尽快发送出去,如果客户端和服务器间传输数据量较大,通过可以设置NoDelay为0来开启Nagel算法,缓存一段数据后再进行发送,减少报文数量。
TSocket默认开启了Linger,并设置linger time为0,这样close会丢弃socket发送缓冲区中的数据,并向对端发送一个RST报文,close不会被阻塞,立即返回。
TServerSocket默认关闭了Linger,close不会被阻塞,立即返回。
4. fb303作为handler的基类,里面预置了一些rpc方法,用于监控,包括系统状态,请求次数等状态信息。
thrift文件中需要include "fb303.thrift"这样来将service导入目标thrift文件中。thrift编译后的代码只需要相应的Handler多重继承facebook::fb303::FacebookBase就好了。
  1. class scribeHandler : virtual public scribe::thrift::scribeIf,
  2.                               public facebook::fb303::FacebookBase {
5. 可以将ZeroMQ等作为transport使用其zeromq来进行通讯。
 可以参考thrift-0.7.0/contrib/zeromq中的代码。【未进行测试】
https://issues.apache.org/jira/browse/THRIFT-812
6. thrift支持完全async,生成代码的时候需要使用
 thrift --gen cpp:cob_style xxx.thrift
这样的话,生成的代码需要TEventServer.h,但是async目录下没有,只有TEvhttpServer.h
https://github.com/klickverbot/thrift/commit/5ddabb8e3f63a15874e436c9a650dc17f7dd7028#diff-2
 
【注意】async有些问题,编译自动生成的代码需要TEventServer.h(0.7.0和svn trunk都是如此),但是thrift中没有这个文件。contrib/async中有一个http的异步测试代码,大致看了一下实现,使用std::tr1::function和std::tr1::bind实现完成回调函数,实现Processor的异步处理,增加了TAsyncProcessor,其process函数返回的时候,真正的逻辑可能没有完成,依赖完成回调函数处理请求完成的部分,对于服务器端来讲主要是将response发送给客户端。一般RPC业务同步的TProcessor就可以了,只有类似proxy这种中间服务需要异步处理,不过当前thrift中只有TEvhttpServer可用;-)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值