SpringBoot集成Thrift實(shí)現(xiàn)RPC服務(wù)基礎(chǔ)示例-Java服務(wù)端和客戶端

項(xiàng)目需要使用跨語言RPC服務(wù)辖所,RPC這里就不多講了,跨語言的框架目前比較有名的有Thrift磨德、gRPC缘回、Hessian這幾個。

  • Thrift原來是Facebook為大規(guī)模跨語言RPC服務(wù)開發(fā)的項(xiàng)目酥宴,后臺捐給Apache了揩环,官網(wǎng)鏈接https://thrift.apache.org/

  • gRPC是Google開發(fā)的高性能幅虑、通用的開源RPC框架丰滑,官網(wǎng)鏈接https://www.grpc.io/

  • Hessian是一個輕量級的使用二進(jìn)制序列化的的RPC框架,基于HTTP倒庵,所以不存在什么語言問題褒墨,官網(wǎng)鏈接http://hessian.caucho.com

對于這三個框架的性能比較網(wǎng)上很多文章分析,總的來說還是Thrift性能占優(yōu)勢一點(diǎn)擎宝,但是Thrift官方維護(hù)并不太好郁妈,最新版本還是2018年12月發(fā)布的,而且github上連issue都沒開绍申,Hessian也是更新慢噩咪,而gRPC更新活躍,用的人也比較多极阅。至于使用哪個看個人吧胃碾。

說明一點(diǎn):Thrift有BIO(阻塞IO)方式和NIO(非阻塞IO)方式,下面先講BIO方式筋搏,然后再說NIO的仆百。

下面進(jìn)入主題:

一、下載Thrift接口生成工具

Windows版本下載地址:https://mirrors.tuna.tsinghua.edu.cn/apache/thrift/0.11.0/ (清華大學(xué)開源軟件鏡像站)

其他版本源碼下載地址:https://thrift.apache.org/download#maven-artifact 或者 https://github.com/apache/thrift
安裝文檔:https://thrift.apache.org/docs/install/ 或者 https://github.com/apache/thrift/tree/master/doc/install

我這里用的的是Windows版本的exe奔脐,直接拿來用俄周,下載后可以把路徑加入到系統(tǒng)Path里面這樣在cmd里面就可以直接使用thrift命令來操作了。其他系統(tǒng)的安裝方法請參考文檔髓迎。

\color{red}{注意:這里的版本必須是0.11版本的}
因?yàn)樽钚?.12版本有一個錯誤峦朗,在使用BIO多線程的情況下會一直打印一個異常,雖然并不影響服務(wù)排龄,但是每次都會打印一個Error日志很不好波势,錯誤情況這篇文章
后面我也會說NIO方式,是可以直接用0.12版本沒問題

二涣雕、創(chuàng)建TestService.thrift文件

thrift文件規(guī)范說明請看這篇文章艰亮。

創(chuàng)建一個很簡單的示例文件

namespace java com.example.thrift.service

service TestService {
    string sayHello(1:string message);
}

三闭翩、生成Java代碼

在thrift文件目錄下使用命令

thrift --gen java TestService.thrift

執(zhí)行完之后挣郭,會在TestService.thrift目錄下生成一個gen-java目錄,里面有個生成好的TestService.java文件疗韵,這個就是用來實(shí)現(xiàn)遠(yuǎn)程調(diào)用的接口兑障,接下來把生成的Java文件復(fù)制到項(xiàng)目源碼里面。這里執(zhí)行命令可以直接在IDEA下面工具欄里的Terminal選項(xiàng)卡里面執(zhí)行,首先要進(jìn)入到TestService.thrift的目錄下面流译,然后直接執(zhí)行上面的命令就可以了逞怨,不用去切換CMD了。


TestService.thrift和生成的Java文件

四福澡、SpringBoot中引入Thrift

maven artifact:

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

這里也要注意引入的版本跟上面生成代碼的工具版本要一致叠赦,這里也使用0.11的版本。然后看一下整個項(xiàng)目的結(jié)構(gòu):


示例代碼結(jié)構(gòu)

加下來看幾個文件的代碼革砸。

五除秀、實(shí)現(xiàn)服務(wù)接口

上面生成的TestService源碼復(fù)制到service包下,然后在這里新建接口實(shí)現(xiàn)類TestServiceImpl:

package com.example.thrift.service;

import lombok.extern.slf4j.Slf4j;
import org.apache.thrift.TException;

@Slf4j
public class TestServiceImpl implements TestService.Iface {
    @Override
    public String sayHello(String message) throws TException {
        log.info("<<<收到消息:{}", message);
        return "Hi, I'm Server!";
    }
}

六算利、編寫服務(wù)端

在server包下面新建ThriftServer服務(wù)端代碼:

package com.example.thrift.server;

import com.example.thrift.service.TestService;
import com.example.thrift.service.TestServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.thrift.TProcessor;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TServerSocket;

@Slf4j
public class ThriftServer {

    public void start() {
        try {
            log.info(">>>Thrift服務(wù)端開啟");
            TServerSocket serverSocket = new TServerSocket(1998); //服務(wù)端口1998
            TProcessor tProcessor = new TestService.Processor<TestService.Iface>(new TestServiceImpl());

            //BIO單線程版
            //TSimpleServer.Args sArgs = new  TSimpleServer.Args(serverSocket);
            //sArgs.processor(tProcessor);  //注冊服務(wù)
            //TServer server = new TSimpleServer(sArgs);

            //BIO多線程版
            TThreadPoolServer.Args tArgs = new TThreadPoolServer.Args(serverSocket);
            tArgs.processor(tProcessor); //注冊服務(wù)
            tArgs.minWorkerThreads(2); //設(shè)置線程池核心線程數(shù)量
            tArgs.maxWorkerThreads(10); //設(shè)置線程池最大線程數(shù)量
            TServer server = new TThreadPoolServer(tArgs);

            server.serve();
        } catch (Exception e) {
            log.error("Thrift服務(wù)發(fā)生錯誤:", e);
        }
    }
}

注意Thrift提供了兩種線程版本册踩,一個是單線程的一個是多線程的。另外一點(diǎn)要注意的是服務(wù)端server是會阻塞主線程的效拭,所以在server開啟之后不要有任務(wù)其他操作暂吉,否則是運(yùn)行不到的。在SpringBoot初始化的過程中也不要啟動服務(wù)缎患,因?yàn)镾pringBoot默認(rèn)是以類名稱排序去掃描包下面的配置或者組件等等慕的,如果Thrift服務(wù)類名稱在其他配置前面,剛好你在初始化Thrift的時候順便也啟動了服務(wù)的話挤渔,那么其他的Spring配置就會不能初始化了业稼,所以可以在SpringBoot完全初始化之后再啟動Thrift,或者干脆起一個線程跑Thrift服務(wù)蚂蕴。
這里的單線程是共用主線程就是main線程低散,多線程則是另外開啟一個ThreadPoolExecutor線程池來處理socket的。這個看TThreadPoolServer源代碼就知道了:


TThreadPoolServer初始化源碼片段

七骡楼、編寫客戶端

在client包下面創(chuàng)建ThriftClient熔号,為了簡單,這里只用一個簡單Java類來當(dāng)做客戶端鸟整,代碼很少:

package com.example.thrift.client;

import com.example.thrift.service.TestService;
import lombok.extern.slf4j.Slf4j;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;

@Slf4j
public class ThriftClient {
    public static void main(String[] args) {
        TTransport transport = null;
        try {
            //TSocket 參數(shù)分別是:服務(wù)器地址引镊,端口,連接超時時間(毫秒)
            transport = new TSocket("localhost", 1998, 30000);
            TProtocol protocol = new TBinaryProtocol(transport);
            TestService.Client client = new TestService.Client(protocol);
            transport.open();

           //這里得到返回數(shù)據(jù)是同步的
            String reply = client.sayHello("Hello, I'm Client");
            log.info(">>>收到回復(fù)消息:{}", reply);

            //如果需要異步篮条,不要返回數(shù)據(jù)可以使用send_***方法
            //client.send_sayHello("Hello, I'm Client ~ ");
            //注意recv_***方法是同步的弟头,會阻塞到收完數(shù)據(jù)
            //String reply = client.recv_sayHello();
            //log.info(">>>收到回復(fù)消息:{}", reply);

        } catch (Exception e) {
            log.error("Thrift客戶端發(fā)生錯誤", e);
        } finally {
            if (transport != null) {
                transport.close();
            }
        }
    }
}

好了,BIO部分就到這里涉茧。下面來說說NIO部分赴恨,其實(shí)也差不多,只是換了個類“樗ǎ現(xiàn)在可以把Thrift的版本改到最新版本0.12.0伦连,接口生產(chǎn)工具Windows下載地址只要把0.11.0的地址改成0.12.0就可以了https://mirrors.tuna.tsinghua.edu.cn/apache/thrift/0.12.0/雨饺,對應(yīng)的maven資源版本也需要改成0.12.0,因?yàn)?.12版工具生成的代碼跟0.11版的有點(diǎn)不一樣惑淳,不過0.11版改一下也是可以用额港,但是最好還是跟著版本匹配來。下面的NIO的都是基于0.12版的

八歧焦、創(chuàng)建NIO服務(wù)端

在server包下創(chuàng)建ThriftNIOServer:

package com.example.thrift.server;

import com.example.thrift.service.TestService;
import com.example.thrift.service.TestServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.thrift.TProcessor;
import org.apache.thrift.server.THsHaServer;
import org.apache.thrift.server.TNonblockingServer;
import org.apache.thrift.transport.TNonblockingServerSocket;

@Slf4j
public class ThriftNIOServer {
    public void start() {
        try {
            log.info(">>>Thrift NIO 服務(wù)端開啟");
            TProcessor tprocessor = new TestService.Processor<TestService.Iface>(new TestServiceImpl());
            TNonblockingServerSocket serverSocket = new TNonblockingServerSocket(1998);
            //NIO單線程版
            //TNonblockingServer.Args args = new TNonblockingServer.Args(serverSocket);
            //args.processor(tprocessor);
            //TNonblockingServer server = new TNonblockingServer(args);

            //NIO多線程版
            THsHaServer.Args args = new THsHaServer.Args(serverSocket);
            args.processor(tprocessor);
            args.minWorkerThreads(2);
            args.maxWorkerThreads(10);
            THsHaServer server = new THsHaServer(args);
            server.serve();
        } catch (Exception e) {
            log.error("Thrift服務(wù)端發(fā)生錯誤:", e);
        }
    }
}

這里也有單線程和多線程版本移斩。要注意的一點(diǎn)是NIO版本的transport默認(rèn)必須用TFramedTransport,所以Client里面也要用同樣的transport绢馍,否則服務(wù)端直接打印一個錯誤信息叹哭,不會拋異常,只是提醒客戶端使用的transport不正確:

ERROR 14012 --- [       Thread-5] .s.AbstractNonblockingServer$FrameBuffer : Read an invalid frame size of -2147418111. Are you using TFramedTransport on the client side?

客戶端則會直接拋異常痕貌,因?yàn)榉?wù)端根本就沒處理請求风罩。

九、創(chuàng)建NIO客戶端

在client包下創(chuàng)建ThriftNIOClient:

package com.example.thrift.client;

import com.example.thrift.service.TestService;
import lombok.extern.slf4j.Slf4j;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;

@Slf4j
public class ThriftNIOClient {
    public static void main(String[] args) {
        TTransport transport = null;
        try {
            //TSocket 參數(shù)分別是:服務(wù)器地址舵稠,端口超升,連接超時時間(毫秒),與BIO比較哺徊,只有這里不同室琢,
            //就是在創(chuàng)建transport 的時候,包了一層TFramedTransport落追,和服務(wù)端匹配起來
            transport = new TFramedTransport(new TSocket("localhost", 1998, 30000));
            TProtocol protocol = new TBinaryProtocol(transport);
            TestService.Client client = new TestService.Client(protocol);
            transport.open();

            //這里得到返回數(shù)據(jù)是同步的
            String reply = client.sayHello("Hello, I'm NIO Client");
            log.info(">>>收到回復(fù)消息:{}", reply);

            //如果需要異步盈滴,不要返回數(shù)據(jù)可以使用send_***方法
            //client.send_sayHello("Hello, I'm Client ~ ");
            //注意recv_***方法是同步的,會阻塞到收完數(shù)據(jù)
            //String replyAsync = client.recv_sayHello();

        } catch (Exception e) {
            log.error("Thrift客戶端發(fā)生錯誤", e);
        } finally {
            //NIO版本這一定要注意關(guān)閉transport
            if (transport != null) {
                transport.close();
            }
        }
    }
}

在這里使用0.12的版本一切都很正常轿钠,如果換成是BIO多線程方式巢钓,這個版本會一直打印一個錯誤。

十疗垛、總結(jié)

總的來說Thrift的使用還是簡單的症汹,接口代碼可以生成不同語言平臺的代碼,這樣一個服務(wù)端可以對應(yīng)各種不同語言的客戶端贷腕,對于小型項(xiàng)目RPC來說背镇,確實(shí)是很方便的事情,至于服務(wù)治理泽裳、分布式這里就不討論了瞒斩。
在生產(chǎn)環(huán)境下服務(wù)端最好還是采用NIO方式。

====> 本文源碼

好了涮总,到此本文結(jié)束胸囱,感謝你的閱讀~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市妹卿,隨后出現(xiàn)的幾起案子旺矾,更是在濱河造成了極大的恐慌,老刑警劉巖夺克,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件箕宙,死亡現(xiàn)場離奇詭異,居然都是意外死亡铺纽,警方通過查閱死者的電腦和手機(jī)柬帕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狡门,“玉大人陷寝,你說我怎么就攤上這事∑淞螅” “怎么了凤跑?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長叛复。 經(jīng)常有香客問我仔引,道長,這世上最難降的妖魔是什么褐奥? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任咖耘,我火速辦了婚禮,結(jié)果婚禮上撬码,老公的妹妹穿的比我還像新娘儿倒。我一直安慰自己,他們只是感情好呜笑,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布夫否。 她就那樣靜靜地躺著,像睡著了一般叫胁。 火紅的嫁衣襯著肌膚如雪慷吊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天曹抬,我揣著相機(jī)與錄音溉瓶,去河邊找鬼。 笑死谤民,一個胖子當(dāng)著我的面吹牛堰酿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播张足,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼触创,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了为牍?” 一聲冷哼從身側(cè)響起哼绑,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤岩馍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后抖韩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛀恩,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年茂浮,在試婚紗的時候發(fā)現(xiàn)自己被綠了双谆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡席揽,死狀恐怖顽馋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情幌羞,我是刑警寧澤寸谜,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站属桦,受9級特大地震影響程帕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜地啰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一愁拭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧亏吝,春花似錦岭埠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至止喷,卻和暖如春馆类,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弹谁。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工乾巧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人预愤。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓沟于,卻偏偏與公主長得像,于是被迫代替她去往敵國和親植康。 傳聞我的和親對象是個殘疾皇子旷太,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評論 2 348