Java學(xué)習(xí)筆記(十)——Thrift入門及一些基礎(chǔ)知識(shí)介紹

公司跨項(xiàng)目協(xié)作带迟,一些部門服務(wù)框架底層封裝了thrift提供服務(wù)券盅,于是對(duì)thrift簡(jiǎn)單做了一些了解鞭执。

關(guān)于thrift

Facebook公布的一款開(kāi)源跨語(yǔ)言的RPC框架。

什么是RPC框架呢伴找?RPC全稱為Remote Procedure Call,意為遠(yuǎn)程過(guò)程調(diào)用盈蛮。

其實(shí)簡(jiǎn)單來(lái)說(shuō)有兩個(gè)系統(tǒng),一個(gè)系統(tǒng)想調(diào)用另一個(gè)系統(tǒng)技矮,但兩個(gè)系統(tǒng)不在同一個(gè)進(jìn)程抖誉,需要通過(guò)網(wǎng)絡(luò)來(lái)傳輸,而網(wǎng)絡(luò)傳輸需要涉及Socket,序列化反序列化,網(wǎng)絡(luò)I/O等一系列的事項(xiàng)衰倦,牛掰的程序員將這一過(guò)程封裝起來(lái)做成了一個(gè)框架袒炉,就是RPC框架,而thrift是其中一種樊零。

thrift通過(guò)一個(gè)中間語(yǔ)言IDL(接口定義語(yǔ)言)來(lái)定義RPC的數(shù)據(jù)類型和接口,這些內(nèi)容寫在以.thrift結(jié)尾的文件中,然后通過(guò)特殊的編譯器來(lái)生成不同語(yǔ)言的代碼,以滿足不同需要的開(kāi)發(fā)者,比如java開(kāi)發(fā)者,就可以生成java代碼,c++ 開(kāi)發(fā)者可以生成c++ 代碼,生成的代碼中不但包含目標(biāo)語(yǔ)言的接口定義,方法,數(shù)據(jù)類型,還包含有RPC協(xié)議層和傳輸層的實(shí)現(xiàn)代碼我磁。

thrift的協(xié)議棧結(jié)構(gòu)

安裝thrift

安裝前的小插曲

一開(kāi)始打算用Homebrew裝的,想象Homebrew好久沒(méi)更新了驻襟,首先更新了一把brew update,結(jié)果mac居然報(bào)了下面的錯(cuò)誤:

invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at:

google了一下發(fā)現(xiàn)是因?yàn)閙acOS本身升級(jí)造成的夺艰,需要安裝下Command line tools,調(diào)用下命令即可沉衣,但是非常慢郁副,裝了幾十分鐘。

xcode-select --install

image

當(dāng)我解決了這個(gè)問(wèn)題之后豌习,發(fā)現(xiàn)用brew裝只能裝最新版本的存谎,但由于提供方目前使用的是0.9.3的版本,為了保證一致斑鸦,所以打算跟他們一樣愕贡,所以放棄了使用brew使用草雕。

mac下正式安裝

首先到官網(wǎng)下載對(duì)應(yīng)版本的安裝包,選擇對(duì)應(yīng)的版本就可以了巷屿。

image

下載下來(lái)之后,解壓后進(jìn)入到目錄進(jìn)行如下操作:

#Step 1
./configure --prefix=/usr/local/ --with-boost=/usr/local --with-libevent=/usr/local --without-ruby --without-perl --without-php --without-nodejs

#Step 2
make

#Step 3
make install 

在執(zhí)行第一步的時(shí)候發(fā)現(xiàn)報(bào)錯(cuò)了墩虹,錯(cuò)誤如下:

configure: error: Bison version 2.5 or higher must be installed on the system!

原因是mac上預(yù)安裝bison版本過(guò)低嘱巾,需要升級(jí)下bison,直接通過(guò)homebrew安裝即可:

brew install bison

安裝后還需要替換一下路徑诫钓,默認(rèn)安裝的路徑是在:

/usr/local/opt/bison/bin/bison

而系統(tǒng)自帶時(shí)的路徑是在:

/Library/Developer/CommandLineTools/usr/bin/

將原來(lái)的bison重命名下旬昭,然后將新的bison復(fù)制進(jìn)去:

mv bison bison_copy

cp /usr/local/opt/bison/bin/bison /Library/Developer/CommandLineTools/usr/bin/

這樣重復(fù)上面的步驟即可,安裝后可以查看下版本菌湃,如果正常展示就說(shuō)明安裝成功了:

thrift -version

數(shù)據(jù)類型及關(guān)鍵字

基本類型

thrift不支持無(wú)符號(hào)的類型,無(wú)符號(hào)類型可以簡(jiǎn)單理解為不能表示負(fù)數(shù),只能表示正數(shù)的類型,像java的基本數(shù)據(jù)類型都是有符號(hào)的類型问拘。

byte:有符號(hào)字節(jié)
i32:32位有符號(hào)整數(shù),此外還有i16,i64
double:64位浮點(diǎn)數(shù)
string:二進(jìn)制字符串
bool 布爾值 true或false

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

類似于c語(yǔ)言的結(jié)構(gòu)體定義,在java中會(huì)被轉(zhuǎn)化為javabean類。

struct DemoModel {
  1: i32 id;
  2: string name;
  3: double number;
  4: bool flag;
}

服務(wù)類型(service)

service:對(duì)應(yīng)服務(wù)的接口,內(nèi)部可定義各種方法,相當(dāng)于java中創(chuàng)建interface一樣,創(chuàng)建的service經(jīng)過(guò)代碼生成命令會(huì)生成客戶端,服務(wù)端的框架代碼。

service DemoService{
  string demoString(1:string s);
  i32 demoInt(1:i32 i);
  bool demoBoolean(1:bool b);
  void demoVoid();
  string demoNull();
}

異常類型(Exception)

exception RequestException {

  1:i32 code;

  2:string msg;

}

容器類型

集合中的元素可以是除了service之外的任意類型骤坐。

list<T>:有序列表,元素可重復(fù)

set<T>:無(wú)需集合,元素不可重復(fù)

map<K,V>:鍵值對(duì)集合

枚舉類型

enum StatusEnum{

  Success,

  Error

}

命名空間(namespace)

可以理解成java中的packet,用于避免一些代碼沖突,每種語(yǔ)言都有屬于自己的命名空間的方式,比如java語(yǔ)言,就可以使用java語(yǔ)言的格式绪杏。

namespace java com.demo.project

其他常用的

required string name1: 必選參數(shù)
optional string name2: 可選參數(shù)
const string strDemo = "demo": 定義常量
include "demo.thrift" 引入文件

Thrift支持的傳輸協(xié)議

Thrift支持多種傳輸協(xié)議,我們可以根據(jù)自己的需要來(lái)選擇合適的類型,總體上來(lái)說(shuō),分為文本傳輸和二進(jìn)制傳輸,由于二進(jìn)制傳輸在傳輸速率和節(jié)省帶寬上有優(yōu)勢(shì),所以大部分情況下使用二進(jìn)制傳輸是比較好的選擇。

TBinaryProtocol:使用二進(jìn)制編碼格式傳輸,是thrift的默認(rèn)傳輸協(xié)議

TCompactProtocol:使用壓縮格式傳輸

TJSONProtocol :使用JSON格式傳輸

TDebugProtocol : 使用易懂可讀的文本格式進(jìn)行傳輸纽绍,以便于debug

TSimpleJSONProtocol : 提供JSON只寫的協(xié)議蕾久,適用于通過(guò)腳本語(yǔ)言解析

Thrift支持的傳輸模式

Thrift封裝了一層傳輸層來(lái)支持底層的網(wǎng)絡(luò)通信,在Thrift中稱為Transport,不僅提供open,close,flush等方法,還有一些read/write方法。

TSocket:阻塞式IO的Transport實(shí)現(xiàn),用在客戶端.

TServerSocket:非阻塞式Socket,用于服務(wù)器端,用于監(jiān)聽(tīng)TSocket.

TNonblockingSocket:非阻塞式IO的實(shí)現(xiàn)

TMemoryInputTransport: 封裝了一個(gè)字節(jié)數(shù)組byte[]來(lái)做輸入流的封裝

TFramedTransport: 同樣使用非阻塞方式拌夏,按塊的大小進(jìn)行傳輸,輸入流封裝了TMemoryInputTransport 

Thrift支持的服務(wù)模型

TSimpleServer

這種工作模式只有一個(gè)線程,循環(huán)監(jiān)聽(tīng)傳過(guò)來(lái)的請(qǐng)求并對(duì)其進(jìn)行處理,處理完才能接受下一個(gè)請(qǐng)求,是一種阻塞式IO的實(shí)現(xiàn),因?yàn)樾时容^低,實(shí)際線上環(huán)境一般用不到.一般用于開(kāi)發(fā)時(shí)候演示工作流程時(shí)使用僧著。

TNonblockingServer

這種模式與TsimpleServer最大的區(qū)別就是使用NIO,也就是非阻塞是IO的方式實(shí)現(xiàn)IO的多路復(fù)用,它可以同時(shí)監(jiān)聽(tīng)多個(gè)socket的變化,但因?yàn)闃I(yè)務(wù)處理上還是單線程模式,所以在一些業(yè)務(wù)處理比較復(fù)雜耗時(shí)的時(shí)候效率還是不高,因?yàn)槎鄠€(gè)請(qǐng)求任務(wù)依然需要排隊(duì)一個(gè)一個(gè)進(jìn)行處理。

TThreadPoolServer

這種模式引入了線程池,主線程只負(fù)責(zé)accept,即監(jiān)聽(tīng)Socket,當(dāng)有新的請(qǐng)求(客戶端Socket)來(lái)時(shí),就會(huì)在線程池里起一個(gè)線程來(lái)處理業(yè)務(wù)邏輯,這樣在并發(fā)量比較大的時(shí)候(但不超過(guò)線程池的數(shù)量)每個(gè)請(qǐng)求都能及時(shí)被處理,效率比較高,但一旦并發(fā)量很大的時(shí)候(超過(guò)線程池?cái)?shù)量),后面來(lái)的請(qǐng)求也只能排隊(duì)等待障簿。

TThreadedSelectorServer

這是一種多線程半同步半異步的服務(wù)模型,是Thrift提供的最復(fù)雜最高級(jí)的服務(wù)模型,內(nèi)部有一個(gè)專門負(fù)責(zé)處理監(jiān)聽(tīng)Socket的線程,有多個(gè)專門處理業(yè)務(wù)中網(wǎng)絡(luò)IO的線程,有一個(gè)專門負(fù)責(zé)決定將新Socket連接分配給哪一個(gè)線程處理的起負(fù)載均衡作用的線程,還有一個(gè)工作線程池.這種模型既可以響應(yīng)大量并發(fā)連接的請(qǐng)求又可以快速對(duì)網(wǎng)絡(luò)IO進(jìn)行讀寫,能適配很多場(chǎng)景,因此是一種使用比較高頻的服務(wù)模盹愚。

java實(shí)現(xiàn)

通過(guò)一個(gè)小demo來(lái)了解下thrift和具體的編碼實(shí)現(xiàn)。

首先我們創(chuàng)建個(gè)thrift文件,簡(jiǎn)單定義了一個(gè)方法:

namespace java service.demo
service Hello{
    string helloString(1:string para)
}

創(chuàng)建好Hello.thrift后通過(guò)終端生成java的代碼:

thrift -r -gen java Hello.thrift

發(fā)現(xiàn)在當(dāng)前目錄下多了一個(gè)gen-java的目錄,里面的有一個(gè)Hello.java的文件.這個(gè)java文件包含Hello服務(wù)的接口定義Hello.Iface,以及服務(wù)調(diào)用的底層通信細(xì)節(jié),包括客戶端的調(diào)用邏輯Hello.Client以及服務(wù)端的處理邏輯Hello.Processor站故。

接著可以引入jar包杯拐,然后將生成的代碼復(fù)制到項(xiàng)目中:

<dependency>
     <groupId>org.apache.thrift</groupId>
     <artifactId>libthrift</artifactId>
     <version>0.9.3</version>
</dependency>

然后創(chuàng)建HelloServiceImpl實(shí)現(xiàn)Hello.Iface接口:

package service.demo;
import org.apache.thrift.TException;

public class HelloServiceImpl implements Hello.Iface {
    public String helloString(String para) throws TException {
        return "result:"+para;
    }
}

接著創(chuàng)建服務(wù)端實(shí)現(xiàn)代碼HelloServiceServer,把HelloServiceImpl作為一個(gè)具體的處理器傳遞給Thrift服務(wù)器:

package service.demo;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;


public class HelloServiceServer {
    public static void main(String[] args) {
        try {
            System.out.println("服務(wù)端開(kāi)啟....");
            TProcessor tprocessor = new Hello.Processor<Hello.Iface>(new HelloServiceImpl());
            // 簡(jiǎn)單的單線程服務(wù)模型
            TServerSocket serverTransport = new TServerSocket(9898);
            TServer.Args tArgs = new TServer.Args(serverTransport);
            tArgs.processor(tprocessor);
            tArgs.protocolFactory(new TBinaryProtocol.Factory());
            TServer server = new TSimpleServer(tArgs);
            server.serve();
            }catch (TTransportException e) {
            e.printStackTrace();
        }
    }
}

最后創(chuàng)建客戶端實(shí)現(xiàn)代碼HelloServiceClient,調(diào)用Hello.client訪問(wèn)服務(wù)端的邏輯實(shí)現(xiàn)

package service.demo;

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 org.apache.thrift.transport.TTransportException;


public class HelloServiceClient {

    public static void main(String[] args) {
        System.out.println("客戶端啟動(dòng)....");
        TTransport transport = null;
        try {
            transport = new TSocket("localhost", 9898, 30000);
            // 協(xié)議要和服務(wù)端一致
            TProtocol protocol = new TBinaryProtocol(transport);
            Hello.Client client = new Hello.Client(protocol);
            transport.open();
            String result = client.helloString("哈哈");
            System.out.println(result);
        } catch (TTransportException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } finally {
            if (null != transport) {
                transport.close();
            }
        }
    }
}

啟動(dòng)服務(wù)端和客戶端就能看到效果啦。

總結(jié)

簡(jiǎn)單了解了thrift之后就開(kāi)始代碼變現(xiàn)了世蔗,有空還是需要深入了解下的端逼。

另外看到一篇thrift與http性能對(duì)比的文章(小測(cè)thrift和http在node.js中的性能對(duì)比),發(fā)現(xiàn)thrift性能還是可以的污淋,畢竟現(xiàn)在大多情況下我們的服務(wù)還是使用http通過(guò)json傳輸?shù)亩ヌ病H绻麑?duì)于性能有高要求的業(yè)務(wù)場(chǎng)景,可以考慮thrift的寸爆。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末礁鲁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赁豆,更是在濱河造成了極大的恐慌仅醇,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件魔种,死亡現(xiàn)場(chǎng)離奇詭異析二,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)节预,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門叶摄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人安拟,你說(shuō)我怎么就攤上這事蛤吓。” “怎么了糠赦?”我有些...
    開(kāi)封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵会傲,是天一觀的道長(zhǎng)锅棕。 經(jīng)常有香客問(wèn)我,道長(zhǎng)淌山,這世上最難降的妖魔是什么哲戚? 我笑而不...
    開(kāi)封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮艾岂,結(jié)果婚禮上顺少,老公的妹妹穿的比我還像新娘。我一直安慰自己王浴,他們只是感情好脆炎,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著氓辣,像睡著了一般秒裕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钞啸,一...
    開(kāi)封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天几蜻,我揣著相機(jī)與錄音,去河邊找鬼体斩。 笑死梭稚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的絮吵。 我是一名探鬼主播弧烤,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蹬敲!你這毒婦竟也來(lái)了暇昂?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤伴嗡,失蹤者是張志新(化名)和其女友劉穎急波,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瘪校,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡澄暮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了渣淤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赏寇。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吉嫩,死狀恐怖价认,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情自娩,我是刑警寧澤用踩,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布渠退,位于F島的核電站,受9級(jí)特大地震影響脐彩,放射性物質(zhì)發(fā)生泄漏碎乃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一惠奸、第九天 我趴在偏房一處隱蔽的房頂上張望梅誓。 院中可真熱鬧,春花似錦佛南、人聲如沸梗掰。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)及穗。三九已至,卻和暖如春绵载,著一層夾襖步出監(jiān)牢的瞬間埂陆,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工娃豹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留焚虱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓懂版,卻偏偏與公主長(zhǎng)得像著摔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子定续,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

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