Thrift RPC基礎學習總結

一.什麼是Thrift?

Thrift是一個軟件框架,用來進行可擴展且跨語言的服務的開發。它結合了功能強大的軟件堆棧和代碼生成引擎,以構建在 C++、Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、JavaScript、Node.js、Smalltalk、and OCaml 等等編程語言間無縫結合的、高效的服務。php

Thrift最初由facebook開發,07年四月開放源碼,08年5月進入Apache孵化器。Thrift容許你定義一個簡單的定義文件中的數據類型和服務接口。以做爲輸入文件,編譯器生成代碼用來方便地生成RPC客戶端和服務器通訊的無縫跨編程語言。java

官網地址:thrift.apache.orgc++

2、Thrift的基礎

基本類型:apache

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、簡單示例

3.1基本配置ide

到官網下載最新版本,截止今日(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

 

4、注意事項

①thrift服務調用也能夠經過異步方式,須要繼承AsyncIface,而且實現TNonblockingServer模型

②命令中的 -gen的值肯定生成不一樣語言的代碼,如-gen php,-gen java

③thrift須要各類語言的支持庫或者虛擬機才能生成代碼,所以須要下載不一樣的虛擬機SDK,配置相應的環境變量。

相關文章
相關標籤/搜索