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.传输方式:

  1. TSocket:阻塞型 socket,用于客户端,采用系统函数 read 和 write 进行读写数据。
  2. 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();
        }
    }
}

有兴趣的朋友可以下载源码看看。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐