RPC-02-Thrift

1、概述

Thrift是一個軟件框架,用來進行可擴展且跨語言的服務的開發。它結合了功能強大的軟件堆棧和代碼生成引擎,以構建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 等等編程語言間無縫結合的、高效的服務。
Thrift最初由facebook開發,07年四月開放源碼,08年5月進入apache孵化器。thrift允許你定義一個簡單的定義文件中的數據類型和服務接口。以作爲輸入文件,編譯器生成代碼用來方便地生成RPC客戶端和服務器通信的無縫跨編程語言。其傳輸數據採用二進制格式,相對於XML和JSON等序列化方式體積更小,對於高併發、大數據量和多語言的環境更有優勢。
Thrift它含有三個主要的組件:protocol,transport和server,其中,protocol定義了消息是怎樣序列化的,transport定義了消息是怎樣在客戶端和服務器端之間通信的,server用於從transport接收序列化的消息,根據protocol反序列化之,調用用戶定義的消息處理器,並序列化消息處理器的響應,然後再將它們寫回transport。
官網地址:thrift.apache.org
推薦值得一看的文章:
http://jnb.ociweb.com/jnb/jnbJun2009.html
http://wiki.apache.org/thrift
http://thrift.apache.org/static/files/thrift-20070401.pdf

本文代碼可參考:https://github.com/hawkingfoo/thrift-demo

2、下載配置

到官網下載最新版本,截止到(2015-12-12)最新版本爲0.9.3.
如果是Maven構建項目的,直接在pom.xml 中添加如下內容:

<!-- https://mvnrepository.com/artifact/org.apache.thrift/libthrift -->
<dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.9.3</version>
</dependency>

3、基本概念

3.1 數據類型

  • 基本類型:

    • 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:未知編碼文本或二進制字符串,對應 Java 的 String
  • 結構體類型:

    • struct:定義公共的對象,類似於 C 語言中的結構體定義,在 Java 中是一個 JavaBean
  • 集合類型:

    • list:對應 Java 的 ArrayList
    • set:對應 Java 的 HashSet
    • map:對應 Java 的 HashMap
  • 異常類型:

    • exception:對應 Java 的 Exception
  • 服務類型:

    • service:對應服務的類

3.2 數據傳輸層Transport

  • TSocket —— 使用阻塞式 I/O 進行傳輸,是最常見的模式
  • TFramedTransport —— 使用非阻塞方式,按塊的大小進行傳輸,類似於 Java 中的 NIO,若使用 TFramedTransport 傳輸層,其服務器必須修改爲非阻塞的服務類型
  • TNonblockingTransport —— 使用非阻塞方式,用於構建異步客戶端

3.3 數據傳輸協議Protocol

Thrift 可以讓用戶選擇客戶端與服務端之間傳輸通信協議的類別,在傳輸協議上總體劃分爲文本 (text) 和二進制 (binary) 傳輸協議,爲節約帶寬,提高傳輸效率,一般情況下使用二進制類型的傳輸協議爲多數,有時還會使用基於文本類型的協議,這需要根據項目 / 產品中的實際需求。常用協議有以下幾種:
- TBinaryProtocol —— 二進制編碼格式進行數據傳輸

TProtocol protocol = new TBinaryProtocol(transport);
  • TCompactProtocol —— 高效率的、密集的二進制編碼格式進行數據傳輸
TCompactProtocol protocol = new TCompactProtocol(transport);
  • TJSONProtocol —— 使用 JSON 的數據編碼協議進行數據傳輸
TJSONProtocol protocol = new TJSONProtocol(transport);
  • TSimpleJSONProtocol —— 只提供 JSON 只寫的協議,適用於通過腳本語言解析**
TProtocol protocol = new TSimpleJSONProtocol(transport);

3.4 服務器類型Server

  • TSimpleServer —— 單線程服務器端使用標準的阻塞式 I/O,一般用於測試。
  • TThreadPoolServer —— 多線程服務器端使用標準的阻塞式 I/O,預先創建一組線程處理請求。
  • TNonblockingServer —— 多線程服務器端使用非阻塞式 I/O,服務端和客戶端需要指定 TFramedTransport 數據傳輸的方式。
  • THsHaServer —— 半同步半異步的服務端模型,需要指定爲: TFramedTransport 數據傳輸的方式。它使用一個單獨的線程來處理網絡I/O,一個獨立的worker線程池來處理消息。這樣,只要有空閒的worker線程,消息就會被立即處理,因此多條消息能被並行處理。
  • TThreadedSelectorServer —— TThreadedSelectorServer允許你用多個線程來處理網絡I/O。它維護了兩個線程池,一個用來處理網絡I/O,另一個用來進行請求的處理。當網絡I/O是瓶頸的時候,TThreadedSelectorServer比THsHaServer的表現要好。

4、實例

4.1 準備

新建Maven項目,並且添加thrift依賴

    <dependencies>
        <dependency>
            <groupId>org.apache.thrift</groupId>
            <artifactId>libthrift</artifactId>
            <version>0.9.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.7</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

4.2 編寫IDL接口並生成接口文件

namespace java thrifttest.service

// 計算類型 - 僅限整數四則運算
enum ComputeType {
    ADD = 0;
    SUB = 1;
    MUL = 2;
    DIV = 3;
}

// 服務請求
struct ComputeRequest {
    1:required i64 x;
    2:required i64 y;
    3:required ComputeType computeType;
}

// 服務響應
struct ComputeResponse {
    1:required i32 errorNo;
    2:optional string errorMsg;
    3:required i64 computeRet;
}

service ComputeServer {
    ComputeResponse getComputeResult(1:ComputeRequest request);
}

執行編譯命令:

thrift-0.9.3.exe --gen java computeServer.thrift

4.3 服務端接口實現以及服務啓動

public class ThriftTestImpl implements ComputeServer.Iface {
    private static final Logger logger = LogManager.getLogger(ThriftTestImpl.class);

    public ComputeResponse getComputeResult(ComputeRequest request) {
        ComputeType computeType = request.getComputeType();
        long x = request.getX();
        long y = request.getY();
        logger.info("get compute result begin. [x:{}] [y:{}] [type:{}]", x, y, computeType.toString());
        long begin = System.currentTimeMillis();

        ComputeResponse response = new ComputeResponse();
        response.setErrorNo(0);
        try {
            long ret;
            if (computeType == ComputeType.ADD) {
                ret = add(x, y);
                response.setComputeRet(ret);
            } else if (computeType == ComputeType.SUB) {
                ret = sub(x, y);
                response.setComputeRet(ret);
            } else if (computeType == ComputeType.MUL) {
                ret = mul(x, y);
                response.setComputeRet(ret);
            } else {
                ret = div(x, y);
                response.setComputeRet(ret);
            }
        } catch (Exception e) {
            response.setErrorNo(1001);
            response.setErrorMsg(e.getMessage());
            logger.error("exception:", e);
        }

        long end = System.currentTimeMillis();
        logger.info("get compute result end. [errno:{}] cost:[{}ms]",
                response.getErrorNo(), (end - begin));
        return response;
    }

    public long add(long x, long y) {
        return x + y;
    }
    public long sub(long x, long y) {
        return x - y;
    }
    public long mul(long x, long y) {
        return x * y;
    }
    public long div(long x, long y) {
        return x / y;
    }
}
public class ServerMain {
    private static final Logger logger = LogManager.getLogger(ServerMain.class);

    public static void main(String[] args) {
        try {
            ThriftTestImpl workImpl = new ThriftTestImpl();
            TProcessor tProcessor = new ComputeServer.Processor<ComputeServer.Iface>(workImpl);
            final TNonblockingServerTransport transport = new TNonblockingServerSocket(9000);
            TThreadedSelectorServer.Args ttpsArgs = new TThreadedSelectorServer.Args(transport);
            ttpsArgs.transportFactory(new TFramedTransport.Factory());
            ttpsArgs.protocolFactory(new TBinaryProtocol.Factory());
            ttpsArgs.processor(tProcessor);
            ttpsArgs.selectorThreads(16);
            ttpsArgs.workerThreads(32);
            logger.info("compute service server on port :" + 9000);
            TServer server = new TThreadedSelectorServer(ttpsArgs);
            server.serve();
        } catch (Exception e) {
            logger.error(e);
        }
    }
}

4.4 客戶端訪問

public class ComputeClient {
    private ComputeRequest request;

    public ComputeClient() {
        request = new ComputeRequest();
        request.setX(1);
        request.setY(2);
        request.setComputeType(ComputeType.ADD);
    }

    public static void main(String[] args) {
        TTransport transport = null;
        try {
            System.out.println("***********");
            long begin = System.currentTimeMillis();
            // localhost
            transport = new TFramedTransport(new TSocket("127.0.0.1", 9000));
            transport.open();
            TProtocol protocol = new TBinaryProtocol(transport);
            ComputeServer.Client client = new ComputeServer.Client(protocol);

            //調用client的getComputeResult方法
            ComputeResponse response = client.getComputeResult(new ComputeClient().request);
            System.out.println("cost:[" + (System.currentTimeMillis() - begin) + "ms]");
            System.out.println("***********");
            if (response != null) {
                System.out.println(response.toString());
            }
            transport.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (transport != null)
                transport.close();
        }
    }
}

4.5 整體代碼結構

代碼目錄結構

4.6 測試

服務端:

服務端

客戶端:

客戶端

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章