thrift入門教程

概述

本文是入門教程,想要了解thrift的源碼實現可以移步我的CSDN專欄thrift源碼解析

Thrift最初由Facebook研發,主要用于各個服務之間的RPC通信,支持跨語言,常用的語言比如C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml都支持。Thrift是一個典型的CS(客戶端/服務端)結構,客戶端和服務端可以使用不同的語言開發。既然客戶端和服務端能使用不同的語言開發,那么一定就要有一種中間語言來關聯客戶端和服務端的語言,沒錯,這種語言就是IDL(Interface Description Language)。

Thrift IDL

本節介紹Thrift的接口定義語言,Thrift IDL支持的數據類型包含:

基本類型

thrift不支持無符號類型,因為很多編程語言不存在無符號類型,比如java

  • byte: 有符號字節
  • i16: 16位有符號整數
  • i32: 32位有符號整數
  • i64: 64位有符號整數
  • double: 64位浮點數
  • string: 字符串

容器類型

集合中的元素可以是除了service之外的任何類型,包括exception。

  • list<T>: 一系列由T類型的數據組成的有序列表,元素可以重復
  • set<T>: 一系列由T類型的數據組成的無序集合,元素不可重復
  • map<K, V>: 一個字典結構,key為K類型,value為V類型,相當于Java中的HMap<K,V>

結構體(struct)

就像C語言一樣,thrift也支持struct類型,目的就是將一些數據聚合在一起,方便傳輸管理。struct的定 義形式如下:

struct People {
     1: string name;
     2: i32 age;
     3: string sex;
}

枚舉(enum)

枚舉的定義形式和Java的Enum定義差不多,例如:

enum Sex {
    MALE,
    FEMALE
}

異常(exception)

thrift支持自定義exception,規則和struct一樣,如下:

exception RequestException {
    1: i32 code;
    2: string reason;
}

服務(service)

thrift定義服務相當于Java中創建Interface一樣,創建的service經過代碼生成命令之后就會生成客戶端和服務端的框架代碼。定義形式如下:

service HelloWordService {
     // service中定義的函數,相當于Java interface中定義的函數
     string doAction(1: string name, 2: i32 age);
 }

類型定義

thrift支持類似C++一樣的typedef定義,比如:

typedef i32 Integer
typedef i64 Long

注意,末尾沒有逗號或者分號

常量(const)

thrift也支持常量定義,使用const關鍵字,例如:

const i32 MAX_RETRIES_TIME = 10
const string MY_WEBSITE = "http://qifuguang.me";

末尾的分號是可選的,可有可無,并且支持16進制賦值

命名空間

thrift的命名空間相當于Java中的package的意思,主要目的是組織代碼。thrift使用關鍵字namespace定義命名空間,例如:

namespace java com.winwill.thrift

格式是:namespace 語言名 路徑, 注意末尾不能有分號。

文件包含

thrift也支持文件包含,相當于C/C++中的include,Java中的import。使用關鍵字include定義,例 如:

include "global.thrift"

注釋

thrift注釋方式支持shell風格的注釋,支持C/C++風格的注釋,即#和//開頭的語句都單當做注釋,/**/包裹的語句也是注釋。

可選與必選

thrift提供兩個關鍵字required,optional,分別用于表示對應的字段時必填的還是可選的。例如:

struct People {
    1: required string name;
    2: optional i32 age;
}

表示name是必填的,age是可選的。

生成代碼

知道了怎么定義thirtf文件之后,我們需要用定義好的thrift文件生成我們需要的目標語言的源碼,本文以生成java源碼為例。假設現在定義了如下一個thrift文件:

namespace java com.winwill.thrift

enum RequestType {
   SAY_HELLO,   //問好
   QUERY_TIME,  //詢問時間
}

struct Request {
   1: required RequestType type;  // 請求的類型,必選
   2: required string name;       // 發起請求的人的名字,必選
   3: optional i32 age;           // 發起請求的人的年齡,可選
}

exception RequestException {
   1: required i32 code;
   2: optional string reason;
}

// 服務名
service HelloWordService {
   string doAction(1: Request request) throws (1:RequestException qe); // 可能拋出異常。
}

在終端運行如下命令(前提是已經安裝thrift):

thrift --gen java Test.thrift

則在當前目錄會生成一個gen-java目錄,該目錄下會按照namespace定義的路徑名一次一層層生成文件夾,到gen-java/com/winwill/thrift/目錄下可以看到生成的4個Java類:


目錄結構
目錄結構

可以看到,thrift文件中定義的enum,struct,exception,service都相應地生成了一個Java類,這就是能支持Java語言的基本的框架代碼。

服務端實現

上面代碼生成這一步已經將接口代碼生成了,現在需要做的是實現HelloWordService的具體邏輯,實現的方式就是創建一個Java類,implements com.winwill.thrift.HelloWordService,例如:

package com.winwill.thrift;


import org.apache.commons.lang3.StringUtils;
import org.apache.thrift.TException;

import java.util.Date;

/**
 * @author qifuguang
 * @date 15/9/11 15:53
 */
public class HelloWordServiceImpl implements com.winwill.thrift.HelloWordService.Iface {
    // 實現這個方法完成具體的邏輯。
    public String doAction(com.winwill.thrift.Request request) throws com.winwill.thrift.RequestException, TException {
        System.out.println("Get request: " + request);
        if (StringUtils.isBlank(request.getName()) || request.getType() == null) {
            throw new com.winwill.thrift.RequestException();
        }
        String result = "Hello, " + request.getName();
        if (request.getType() == com.winwill.thrift.RequestType.SAY_HELLO) {
            result += ", Welcome!";
        } else {
            result += ", Now is " + new Date().toLocaleString();
        }
        return result;
    }
}

啟動服務

上面這個就是服務端的具體實現類,現在需要啟動這個服務,所以需要一個啟動類,啟動類的代碼如下:

package com.winwill.thrift;

import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;

import java.net.ServerSocket;

/**
 * @author qifuguang
 * @date 15/9/11 16:07
 */
public class HelloWordServer {
    public static void main(String[] args) throws Exception {
        ServerSocket socket = new ServerSocket(7912);
        TServerSocket serverTransport = new TServerSocket(socket);
        com.winwill.thrift.HelloWordService.Processor processor = new com.winwill.thrift.HelloWordService.Processor(new HelloWordServiceImpl());
        TServer server = new TSimpleServer(processor, serverTransport);
        System.out.println("Running server...");
        server.serve();
    }
}

運行之后看到控制臺的輸出為:

Running server...

客戶端請求

現在服務已經啟動,可以通過客戶端向服務端發送請求了,客戶端的代碼如下:

package com.winwill.thrift;

import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;

/**
 * @author qifuguang
 * @date 15/9/11 16:13
 */
public class HelloWordClient {
    public static void main(String[] args) throws Exception {
        TTransport transport = new TSocket("localhost", 8888);
        TProtocol protocol = new TBinaryProtocol(transport);

        // 創建client
        com.winwill.thrift.HelloWordService.Client client = new com.winwill.thrift.HelloWordService.Client(protocol);

        transport.open();  // 建立連接

        // 第一種請求類型
        com.winwill.thrift.Request request = new com.winwill.thrift.Request()
                .setType(com.winwill.thrift.RequestType.SAY_HELLO).setName("winwill2012").setAge(24);
        System.out.println(client.doAction(request));

        // 第二種請求類型
        request.setType(com.winwill.thrift.RequestType.QUERY_TIME).setName("winwill2012");
        System.out.println(client.doAction(request));

        transport.close();  // 請求結束,斷開連接
    }
}

運行客戶端代碼,得到結果:

Hello, winwill2012, Welcome!
Hello, winwill2012, Now is 2015-9-11 16:37:22

并且此時,服務端會有請求日志:

Running server...
Get request: Request(type:SAY_HELLO, name:winwill2012, age:24)
Get request: Request(type:QUERY_TIME, name:winwill2012, age:24)

可以看到,客戶端成功將請求發到了服務端,服務端成功地將請求結果返回給客戶端,整個通信過程完成。

注意事項

  • 本文為作者個人理解,如理解有誤,請留言相告,感激不盡;
  • 本文是入門教程,想要了解thrift的源碼實現可以移步我的CSDN專欄thrift源碼解析
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容