thrift入門-Android
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();
}
}
}
有興趣的朋友可以下載原始碼看看。