1、什么是thrift?
thirft是Facebook公布的一款开源跨语言的RPC框架。
thrift通过一个中间语言IDL(接口定义语言)来定义RPC的数据类型和接口,这些内容写在以.thrift结尾的文件中,然后通过特殊的编译器来生成不同语言的代码,以满足不同需要的开发者,比如可以生成java代码,生成c++代码,生成的代码中不但包含目标语言的接口定义,方法,数据类型,还包含有RPC协议层和传输层的实现代码。以下就是它支持的数据类型。
1.基本类型
bool:布尔值 (true or false), one byte
i16:16位有符号整型
i32:32位有符号整型
double:64位浮点型
string:编码或者二进制的字符串
2.容器(Containers)
Thrift容器与流行编程语言的容器类型相对应,采用Java泛型风格。它有3种可用容器类型:
list: 元素类型为t1的有序表,容许元素重复。
set:元素类型为t1的无序表,不容许元素重复。
map
namespace java mythrift
enum HotelType {
YILONG,
JINJIANG=2
}
struct Hotel {
1: optional i32 id;
2: optional string hotelname;
3: required HotelType HotelType.YILONG;
}
namespace(可省略):命名空间,一方面他可以解决名字冲突的问题,另一方面可以指定生成文件的路径及包名。
required、optional:规范的struct定义中的每个域均会使用required或者optional关键字进行标识。如果required标识的域没有赋值,Thrift将给予提示;如果optional标识的域没有赋值,该域将不会被序列化传输。
3.传输方式:
- TSocket:阻塞型 socket,用于客户端,采用系统函数 read 和 write 进行读写数据。
- TServerSocket:非阻塞型 socket,用于服务器端,accecpt 到的 socket 类型都是 TSocket(即阻塞型 socket)。
2、安装(windows平台)
官网:http://thrift.apache.org/download
Thrift compiler for Windows (thrift-0.11.0.exe)可执行文件用于在Windows下生成目标语言的桩代码,下载该exe应用程序。
3、开发
该Demo功能为:Android端请求服务端,服务端返回数据给Android端
该Demo不考虑性能、线程、优化等内容,仅仅做最简单的Thrift通讯
通过该Demo,我个人对RPC或Thrift理解就是:客户端和服务端约定好接口,服务端实现接口,客户端根据接口传入参数,远程调用服务端的实现方法,从而实现远程的方法调用。Android中存在的AIDL就类似于RPC。
(1)写接口定义文件.thrift文件
namespace csharp TestLibrary
struct ContextObject{
1:string RequestId,
2:string MainSignal,
3:map<string,string> Params
}
struct ServerResult{
1:i32 Code,
2:string Msg,
3:string Data,
4:string Sign
}
enum Operation{
Increase=1,
Decrease=-1
}
service UserStorage{
ServerResult UpdateStar(1: i64 senderId, 2: i64 star,3: Operation op,4: ContextObject context);
}
service OrderStorage{
ServerResult SendGift(1: i64 senderId, 2: i64 receiveId, 3: i64 star,4: ContextObject context);
}
service ChangeLogStorage{
ServerResult AddLog(1: i64 userId,2: i64 changeStar,3: Operation op,4: ContextObject context);
}
(2)生成目标语言代码文件
把官网下载到的文件thrift-0.11.1.exe和test.thrift放到一个目录(thirft)下
打开cmd命令行窗口,进入到这个目录,执行命令:
C:\Users\admin\Desktop\thirft>thrift-0.11.1.exe -gen java test.thrift
执行成功,在thirft目录下,生成一个gen-java文件夹。
(3)创建工程
新建Android工程,gradle中引入thrift
compile 'org.apache.thrift:libthrift:0.11.0'
把刚才生成的接口文件拷贝到项目中,接下来可以使用thrift进行远程调用了。
总结下它的调用步骤:
// 创建 TTransport传输协议
mTransport = ThriftManager.createTTransport();
// 开启 TTransport
ThriftManager.openTTransport(mTransport);
// 创建客户端
mUserClient = ThriftManager.createClient(mTransport);
// 调用服务
mUserClient.UpdateStar(senderId,star,operation,null);
代码如下:
package com.xy.cjy.thriftclient;
import android.support.test.espresso.core.deps.guava.base.Objects;
import com.xy.cjy.thriftclient.thrift.UserStorage;
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;
/**
* thrift管理类
*
* Created by cjy on 2018/3/29.
*/
public class ThriftManager {
private static int port = 9090;
private static String ip = "192.168.78.92";
/**
* 创建 TTransport
* @return
*/
public static TTransport createTTransport(){
TSocket tscoket = new TSocket(ip, port);
tscoket.setConnectTimeout(3000);
tscoket.setSocketTimeout(3000);
tscoket.setTimeout(5000);
TTransport transport = tscoket;
return transport;
}
/**
* 开启 TTransport
* @param transport
* @throws TTransportException
*/
public static void openTTransport(TTransport transport) throws TTransportException {
if(Objects.equal(transport, null)){
return;
}
transport.open();
}
/**
* 关闭 TTransport
* @param transport
*/
public static void closeTTransport(TTransport transport){
if(Objects.equal(transport, null)){
return;
}
transport.close();
}
/**
* 创建客户端
* @return
*/
public static UserStorage.Client createClient(TTransport transport){
if(Objects.equal(transport, null)){
return null;
}
TProtocol protocol = new TBinaryProtocol(transport);
if(Objects.equal(protocol, null)){
return null;
}
UserStorage.Client client = new UserStorage.Client(protocol);
return client;
}
}
MainActivity.java:
package com.xy.cjy.thriftclient;
import android.os.Handler;
import android.os.Message;
import android.support.test.espresso.core.deps.guava.base.Objects;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.xy.cjy.thriftclient.thrift.ContextObject;
import com.xy.cjy.thriftclient.thrift.Operation;
import com.xy.cjy.thriftclient.thrift.ServerResult;
import com.xy.cjy.thriftclient.thrift.UserStorage;
import org.apache.thrift.TException;
import org.apache.thrift.transport.TTransport;
import java.util.HashMap;
/**
* 测试thrift
* client不关闭,循环加1,9000次耗时 mBtnIncrease
* client不关闭,循环-1,10000次耗时 mBtnDecrease
* 每次open,然后调用updateStar,然后close,这样循环10000次 mBtnOpenClose
* 测试是否能够监控到服务端连接中断:open,不调用方法,服务器连接断开看看 mBtnConnect
* 服务器连接断开了,调用方法看看 mBtnDisConnect
*
* 总结:请求的过程中,如果服务器中断连接,会抛出异常:E/MainActivity: java.net.SocketException: recvfrom failed: ECONNRESET (Connection reset by peer)
*
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private final String TAG = "MainActivity";
private Button mBtnIncrease;
private Button mBtnDecrease;
private Button mBtnOpenClose;
private Button mBtnConnect;
private Button mBtnDisConnect;
private TextView mTvMsg;
private Handler mServerResultHandler;
private TTransport mTransport;
private UserStorage.Client mUserClient;
private StringBuffer mSbResultText;
private boolean mIsThreadRunning = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findControl();
initControl();
}
private void findControl(){
mBtnIncrease = findViewById(R.id.btn_increase_thrift);
mBtnDecrease = findViewById(R.id.btn_decrease_thrift);
mBtnOpenClose = findViewById(R.id.btn_openclose_thrift);
mBtnConnect = findViewById(R.id.btn_connect_thrift);
mBtnDisConnect = findViewById(R.id.btn_disconnect_thrift);
mTvMsg = findViewById(R.id.tv_msg);
}
private void initControl(){
mSbResultText = new StringBuffer();
mServerResultHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String resultData = (String) msg.obj;
mSbResultText.append(resultData+"\n");
mTvMsg.setText(mSbResultText.toString());
}
};
mBtnIncrease.setOnClickListener(this);
mBtnDecrease.setOnClickListener(this);
mBtnOpenClose.setOnClickListener(this);
mBtnConnect.setOnClickListener(this);
mBtnDisConnect.setOnClickListener(this);
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id){
case R.id.btn_increase_thrift:{
btnIncreaseThrift();
break;
}
case R.id.btn_decrease_thrift:{
btnDecreaseThrift();
break;
}
case R.id.btn_openclose_thrift:{
btnOpenCloseThrift();
break;
}
case R.id.btn_connect_thrift:{
btnConnectThrift();
break;
}
case R.id.btn_disconnect_thrift:{
btnDisConnectThrift();
break;
}
}
}
/**
* 初始化Transport
*/
private void initService(){
try {
if (mTransport == null) {
// 创建 TTransport
mTransport = ThriftManager.createTTransport();
// 开启 TTransport
ThriftManager.openTTransport(mTransport);
// 创建客户端
mUserClient = ThriftManager.createClient(mTransport);
// 调用服务
if (Objects.equal(mUserClient, null)) {
Log.e(TAG, "创建客户端失败...");
return;
}
}
} catch (TException e) {
Log.e(TAG,e.getMessage());
}
}
/**
* 加;循环每次+1,循环9000次
* 结果:循环+1 9000次的耗时: 65365
*/
private void increase(){
try {
//1-10 10个用户
long senderId = 1;
//每个用户都有10000个
long star = 1;
Operation operation = Operation.findByValue(1);
ContextObject contextObject = new ContextObject();
//两个属性
contextObject.Params = new HashMap<String, String>();
contextObject.RequestId = "uuid001";
long clickTime = System.currentTimeMillis();
ServerResult result = null;
String resultData ="等待结果中...";
//更新星值
for (int i =0; i < 9000; i++){
star++;
result = mUserClient.UpdateStar(senderId,star,operation,null);
resultData = JsonUtil.objectToString(result);
Log.d(TAG,resultData);
}
long lastTime = System.currentTimeMillis();
String wasteTime = (lastTime-clickTime)+"";
Log.d("increase循环9000次的耗时",wasteTime);
Message message = mServerResultHandler.obtainMessage();
message.obj = "increase循环9000次的耗时:"+wasteTime;
mServerResultHandler.sendMessage(message);
mIsThreadRunning = false;
} catch (TException e) {
Log.e(TAG,e.getMessage());
}
}
/**
* 减:每次-1,循环10000次
* 结果:循环-1 10000次的耗时: 72375
*/
private void decrease(){
try {
//1-10 10个用户
long senderId = 1;
//每个用户都有10000个
long star = 10000;
Operation operation = Operation.findByValue(2);
ContextObject contextObject = new ContextObject();
//两个属性
contextObject.Params = new HashMap<String, String>();
contextObject.RequestId = "uuid001";
long clickTime = System.currentTimeMillis();
ServerResult result = null;
String resultData ="等待结果中...";
//更新星值
for (int i =0; i < 10000; i++){
star--;
result = mUserClient.UpdateStar(senderId,star,operation,null);
resultData = JsonUtil.objectToString(result);
Log.d(TAG,resultData);
}
long lastTime = System.currentTimeMillis();
String wasteTime = (lastTime-clickTime)+"";
Log.d("decrease循环10000次的耗时",wasteTime);
Message message = mServerResultHandler.obtainMessage();
message.obj = "decrease循环10000次的耗时:"+wasteTime;
mServerResultHandler.sendMessage(message);
mIsThreadRunning = false;
} catch (TException e) {
Log.e(TAG,e.getMessage());
}
}
/**
* 每次open,然后调用updateStar,然后close
*/
private void openClose(){
try {
// 创建 TTransport
mTransport = ThriftManager.createTTransport();
// 开启 TTransport
ThriftManager.openTTransport(mTransport);
// 创建客户端
mUserClient = ThriftManager.createClient(mTransport);
// 调用服务
if (Objects.equal(mUserClient, null)) {
Log.e(TAG, "创建客户端失败...");
return;
}
//1-10 10个用户
long senderId = 1;
//每个用户都有10000个
long star = 1;
Operation operation = Operation.findByValue(1);
ContextObject contextObject = new ContextObject();
//两个属性
contextObject.Params = new HashMap<String, String>();
contextObject.RequestId = "uuid001";
ServerResult result = null;
String resultData ="等待结果中...";
//更新星值
result = mUserClient.UpdateStar(senderId,star,operation,null);
resultData = JsonUtil.objectToString(result);
Log.d(TAG,resultData);
Message message = mServerResultHandler.obtainMessage();
message.obj = resultData;
mServerResultHandler.sendMessage(message);
} catch (TException e) {
e.printStackTrace();
} finally {
ThriftManager.closeTTransport(mTransport);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mTransport != null) {
// 关闭 TTransport
ThriftManager.closeTTransport(mTransport);
}
if (mServerResultHandler != null){
mServerResultHandler = null;
}
}
private void btnIncreaseThrift(){
if (mIsThreadRunning == false) {
mIsThreadRunning = true;
//发起thrift请求
new Thread(new Runnable() {
@Override
public void run() {
initService();
increase();
}
}).start();
}
}
private void btnDecreaseThrift(){
if (mIsThreadRunning == false) {
mIsThreadRunning = true;
//发起thrift请求
new Thread(new Runnable() {
@Override
public void run() {
initService();
decrease();
}
}).start();
}
}
private void btnOpenCloseThrift(){
if (mIsThreadRunning == false) {
mIsThreadRunning = true;
//发起thrift请求
new Thread(new Runnable() {
@Override
public void run() {
long clickTime = System.currentTimeMillis();
//每次open,然后调用updateStar,然后close,这样循环10000次,看看时间
//结果:open-close循环10000次的耗时: 118265
for (int i = 0; i < 10000; i++) {
openClose();
}
long lastTime = System.currentTimeMillis();
String wasteTime = (lastTime - clickTime) + "";
Log.d("open-close循环10000次的耗时", wasteTime);
Message message = mServerResultHandler.obtainMessage();
message.obj = "open-close循环10000次的耗时:" + wasteTime;
mServerResultHandler.sendMessage(message);
mIsThreadRunning = false;
}
}).start();
}
}
private void btnConnectThrift(){
if (mIsThreadRunning == false) {
mIsThreadRunning = true;
//发起thrift请求
new Thread(new Runnable() {
@Override
public void run() {
initService();
}
}).start();
}
}
private void btnDisConnectThrift(){
if (mIsThreadRunning == false) {
mIsThreadRunning = true;
//发起thrift请求
new Thread(new Runnable() {
@Override
public void run() {
initService();
decrease();
}
}).start();
}
}
}
有兴趣的朋友可以下载源码看看。