thrift入門教程

概述

本文是入門教程,想要了解thrift的源碼實(shí)現(xiàn)可以移步我的CSDN專欄thrift源碼解析

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

Thrift IDL

本節(jié)介紹Thrift的接口定義語言,Thrift IDL支持的數(shù)據(jù)類型包含:

基本類型

thrift不支持無符號(hào)類型,因?yàn)楹芏嗑幊陶Z言不存在無符號(hào)類型,比如java

  • byte: 有符號(hào)字節(jié)
  • i16: 16位有符號(hào)整數(shù)
  • i32: 32位有符號(hào)整數(shù)
  • i64: 64位有符號(hào)整數(shù)
  • double: 64位浮點(diǎn)數(shù)
  • string: 字符串

容器類型

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

  • list<T>: 一系列由T類型的數(shù)據(jù)組成的有序列表,元素可以重復(fù)
  • set<T>: 一系列由T類型的數(shù)據(jù)組成的無序集合,元素不可重復(fù)
  • map<K, V>: 一個(gè)字典結(jié)構(gòu),key為K類型,value為V類型,相當(dāng)于Java中的HMap<K,V>

結(jié)構(gòu)體(struct)

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

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

枚舉(enum)

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

enum Sex {
    MALE,
    FEMALE
}

異常(exception)

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

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

服務(wù)(service)

thrift定義服務(wù)相當(dāng)于Java中創(chuàng)建Interface一樣,創(chuàng)建的service經(jīng)過代碼生成命令之后就會(huì)生成客戶端和服務(wù)端的框架代碼。定義形式如下:

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

類型定義

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

typedef i32 Integer
typedef i64 Long

注意,末尾沒有逗號(hào)或者分號(hào)

常量(const)

thrift也支持常量定義,使用const關(guān)鍵字,例如:

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

末尾的分號(hào)是可選的,可有可無,并且支持16進(jìn)制賦值

命名空間

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

namespace java com.winwill.thrift

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

文件包含

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

include "global.thrift"

注釋

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

可選與必選

thrift提供兩個(gè)關(guān)鍵字required,optional,分別用于表示對應(yīng)的字段時(shí)必填的還是可選的。例如:

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

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

生成代碼

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

namespace java com.winwill.thrift

enum RequestType {
   SAY_HELLO,   //問好
   QUERY_TIME,  //詢問時(shí)間
}

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

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

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

在終端運(yùn)行如下命令(前提是已經(jīng)安裝thrift):

thrift --gen java Test.thrift

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


目錄結(jié)構(gòu)
目錄結(jié)構(gòu)

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

服務(wù)端實(shí)現(xiàn)

上面代碼生成這一步已經(jīng)將接口代碼生成了,現(xiàn)在需要做的是實(shí)現(xiàn)HelloWordService的具體邏輯,實(shí)現(xiàn)的方式就是創(chuàng)建一個(gè)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 {
    // 實(shí)現(xiàn)這個(gè)方法完成具體的邏輯。
    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;
    }
}

啟動(dòng)服務(wù)

上面這個(gè)就是服務(wù)端的具體實(shí)現(xiàn)類,現(xiàn)在需要啟動(dòng)這個(gè)服務(wù),所以需要一個(gè)啟動(dòng)類,啟動(dòng)類的代碼如下:

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();
    }
}

運(yùn)行之后看到控制臺(tái)的輸出為:

Running server...

客戶端請求

現(xiàn)在服務(wù)已經(jīng)啟動(dòng),可以通過客戶端向服務(wù)端發(fā)送請求了,客戶端的代碼如下:

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);

        // 創(chuàng)建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();  // 請求結(jié)束,斷開連接
    }
}

運(yùn)行客戶端代碼,得到結(jié)果:

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

并且此時(shí),服務(wù)端會(huì)有請求日志:

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

可以看到,客戶端成功將請求發(fā)到了服務(wù)端,服務(wù)端成功地將請求結(jié)果返回給客戶端,整個(gè)通信過程完成。

注意事項(xiàng)

  • 本文為作者個(gè)人理解,如理解有誤,請留言相告,感激不盡;
  • 本文是入門教程,想要了解thrift的源碼實(shí)現(xiàn)可以移步我的CSDN專欄thrift源碼解析
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容