一.什么是Thrift?
Thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++、Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、JavaScript、Node.js、Smalltalk、and OCaml 等等编程语言间无缝结合的、高效的服务。
Thrift最初由facebook开发,07年四月开放源码,08年5月进入Apache孵化器。Thrift允许你定义一个简单的定义文件中的数据类型和服务接口。以作为输入文件,编译器生成代码用来方便地生成RPC客户端和服务器通信的无缝跨编程语言。
官网地址:thrift.apache.org
二、Thrift的基础
基本类型:
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
结构体类型:
struct:定义公共的对象,类似于 C 语言中的结构体定义,在 Java 中是一个 JavaBean
容器类型:
list:对应 Java 的 ArrayList
set:对应 Java 的 HashSet
map:对应 Java 的 HashMap
异常类型:
exception:对应 Java 的 Exception
服务类型:
service:对应服务的类
服务端编码基本步骤
- 实现服务处理接口impl
- 创建TProcessor
- 创建TServerTransport
- 创建TProtocol
- 创建TServer
- 启动Server
客户端编码基本步骤
- 创建Transport
- 创建TProtocol
- 基于TTransport和TProtocol创建Client
- 调用Client的相应方法
数据传输协议
- TBinaryProtocol 二进制格式
- TCompactProtocol 压缩格式
- TJSONProtocol JSON格式
- TSimpleJSONProtocol 提供JSON只写协议,生成的文件很容易通过脚本语言解析
提示:客户端和服务端的协议要一致
基本关键字
- oneway : 表示客户端调用服务器不关心服务器的返回值,不关心服务器程序是否执行完成
- void : 和oneway一样没有返回值,但是会确保服务器程序执行完成
- extends:继承,service之间可以通过extends继承
三、简单示例
3.1基本配置
到官网下载最新版本,截止今日(2016-04-23)最新版本为0.9.3
- 如果是Maven构建项目的,直接在pom.xml 中添加如下内容:
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.9.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
- 也可以手动配置jar包,在项目中添加如下jar包即可
3.2 下载thrift并且配置环境变量
下载thrift 工具,将thrift-x.x.x.exe修改完thrift.exe,并且将次程序所在位置添加到path环境变量中。
3.3 生成简单示例
shared.thrift
namespace cpp com.itest.thrift.shared #c++中的命名空间
namespace java com.itest.thrift.shared #java 包名
namespace php com.itest.thrift.shared #php命名空间
struct SharedStruct {
1: i32 key
2: string value
}
service SharedService {
SharedStruct getStruct(1: i32 key)
}
tutorial.thrift
include "shared.thrift" #引入shared.thrift
namespace cpp com.itest.thrift.service
namespace java com.itest.thrift.service
namespace php com.itest.thrift.service
/**
* 定义c风格的数据类型
*
*/
typedef i32 MyInteger
/**
* 定义常量
*
*/
const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}
/**
*
* 定义枚举
*/
enum Operation { //php默认不支持枚举,但这里也会通过其他方式生成php枚举
ADD = 1,
SUBTRACT = 2,
MULTIPLY = 3,
DIVIDE = 4
}
/**
*
* 定义结构体,在php和java中生成bean,在c++中是结构体
*/
struct Work {
1: i32 num1 = 0,
2: i32 num2,
3: Operation op,
4: optional string comment,
}
/**
* 定义异常类型,扩展性很强
*/
exception InvalidOperation {
1: i32 whatOp,
2: string why
}
/**
* 定义服务,这里也可以继承其他服务
*/
service Calculator extends shared.SharedService {
/**
* 定义服务方法
*/
void ping(),
i32 add(1:i32 num1, 2:i32 num2),
i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),
/**
* 此方法表示客户端只发送请求,不关心程序的执行和执行结果,和void不同,void需要确认程序执行结束
*/
oneway void zip()
}
3.4生成代码
执行如下命令(更多命令,请执行 thrift --help 查询)
thrift -o ./ -out ./ -r -gen java tutorial.thrift
将生成的代码添加到项目中,如下:
3.4 实现接口Iface
package com.itest.thrift.handler;
import java.util.HashMap;
import com.itest.thrift.service.InvalidOperation;
import com.itest.thrift.service.Work;
import com.itest.thrift.service.Calculator;
import com.itest.thrift.shared.SharedStruct;
// Generated code
public class CalculatorHandler implements Calculator.Iface {
private HashMap<Integer,SharedStruct> log;
public CalculatorHandler() {
log = new HashMap<Integer, SharedStruct>();
}
public void ping() {
System.out.println("ping()");
}
public int add(int n1, int n2) {
System.out.println("add(" + n1 + "," + n2 + ")");
return n1 + n2;
}
public int calculate(int logid, Work work) throws InvalidOperation {
System.out.println("calculate(" + logid + ", {" + work.op + "," + work.num1 + "," + work.num2 + "})");
int val = 0;
switch (work.op) {
case ADD:
val = work.num1 + work.num2;
break;
case SUBTRACT:
val = work.num1 - work.num2;
break;
case MULTIPLY:
val = work.num1 * work.num2;
break;
case DIVIDE:
if (work.num2 == 0) {
InvalidOperation io = new InvalidOperation();
io.whatOp = work.op.getValue();
io.why = "Cannot divide by 0";
throw io;
}
val = work.num1 / work.num2;
break;
default:
InvalidOperation io = new InvalidOperation();
io.whatOp = work.op.getValue();
io.why = "Unknown operation";
throw io;
}
SharedStruct entry = new SharedStruct();
entry.key = logid;
entry.value = Integer.toString(val);
log.put(logid, entry);
return val;
}
public SharedStruct getStruct(int key) {
System.out.println("getStruct(" + key + ")");
return log.get(key);
}
public void zip() {
System.out.println("zip()");
}
}
3.5发布服务
package com.iuap.thirft.itest;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TServer.Args;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;
import com.itest.thrift.handler.CalculatorHandler;
import com.itest.thrift.service.Calculator;
// Generated code
public class JavaServer {
public static CalculatorHandler handler;
@SuppressWarnings("rawtypes")
public static Calculator.Processor processor;
@SuppressWarnings("rawtypes")
public static void main(String [] args) {
try {
handler = new CalculatorHandler();
processor = new Calculator.Processor(handler);
Runnable simple = new Runnable() {
public void run() {
simple(processor);
}
};
new Thread(simple).start();
} catch (Exception x) {
x.printStackTrace();
}
}
public static void simple(@SuppressWarnings("rawtypes") Calculator.Processor processor) {
try {
TServerTransport serverTransport = new TServerSocket(9090);
TServer server = new TSimpleServer(new Args(serverTransport).processor(processor));
System.out.println("Starting the simple server...");
server.serve();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.6调用服务
package com.iuap.thirft.itest;
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 com.itest.thrift.service.Calculator;
import com.itest.thrift.service.InvalidOperation;
import com.itest.thrift.service.Operation;
import com.itest.thrift.service.Work;
import com.itest.thrift.shared.SharedStruct;
public class JavaClient {
public static void main(String [] args) {
try {
TTransport transport;
// if (args[0].contains("simple")) {
transport = new TSocket("localhost", 9090);
transport.open();
/* }
else {
TSSLTransportParameters params = new TSSLTransportParameters();
params.setTrustStore("../../lib/java/test/.truststore", "thrift", "SunX509", "JKS");
transport = TSSLTransportFactory.getClientSocket("localhost", 9091, 0, params);
}*/
TProtocol protocol = new TBinaryProtocol(transport);
Calculator.Client client = new Calculator.Client(protocol);
perform(client);
transport.close();
} catch (TException x) {
x.printStackTrace();
}
}
private static void perform(Calculator.Client client) throws TException
{
client.ping();
System.out.println("ping()");
int sum = client.add(1,1);
System.out.println("1+1=" + sum);
Work work = new Work();
work.op = Operation.DIVIDE;
work.num1 = 1;
work.num2 = 0;
try {
int quotient = client.calculate(1, work);
System.out.println("Whoa we can divide by 0");
} catch (InvalidOperation io) {
System.out.println("Invalid operation: " + io.why);
}
work.op = Operation.SUBTRACT;
work.num1 = 15;
work.num2 = 10;
try {
int diff = client.calculate(1, work);
System.out.println("15-10=" + diff);
} catch (InvalidOperation io) {
System.out.println("Invalid operation: " + io.why);
}
client.zip();
SharedStruct log = client.getStruct(1);
System.out.println("Check log: " + log.value);
}
}
3.7执行结果
服务器端
Starting the simple server...
ping()
add(1,1)
calculate(1, {DIVIDE,1,0})
calculate(1, {SUBTRACT,15,10})
zip()
getStruct(1)
ping()
add(1,1)
calculate(1, {DIVIDE,1,0})
calculate(1, {SUBTRACT,15,10})
zip()
getStruct(1)
客户端
ping()
1+1=2
Invalid operation: Cannot divide by 0
15-10=5
Check log: 5
四、注意事项
①thrift服务调用也可以通过异步方式,需要继承AsyncIface,并且实现TNonblockingServer模型
②命令中的 -gen的值确定生成不同语言的代码,如-gen php,-gen java
③thrift需要各种语言的支持库或者虚拟机才能生成代码,因此需要下载不同的虚拟机SDK,配置相应的环境变量。
来源:oschina
链接:https://my.oschina.net/u/2256215/blog/1593051