Thrift 的C與golang語(yǔ)言實(shí)現(xiàn)以及相互調(diào)用方式

Thrift 的Go與C語(yǔ)言實(shí)現(xiàn)

Thrift 是Facebook為了解決各系統(tǒng)間大數(shù)據(jù)量的傳輸通信以及系統(tǒng)之間語(yǔ)言環(huán)境不同而設(shè)計(jì)的一種傳輸框架霉撵。目前來(lái)看常用的主流語(yǔ)言Thrift都已經(jīng)很好地支持严沥,并且github上已經(jīng)有很多實(shí)現(xiàn)绕辖,除了C語(yǔ)言之外哼勇。Thrift傳輸?shù)某绦虻撵o態(tài)數(shù)據(jù),即數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)必須事前固定带猴。
Thrift原理就不介紹了吼畏,理論性東西網(wǎng)上很多督赤,并且都是雷同的。下面通過(guò)實(shí)例介紹Thrift 接口在Go與C語(yǔ)言下的實(shí)現(xiàn)泻蚊,以及如何在C語(yǔ)言中調(diào)用Go所編寫(xiě)的Thrift客戶(hù)端躲舌。
  1. thrift 文件編寫(xiě)

#example.thrift

    namespace go thrift.rpc
    struct Response {
    1: required string data;
    }
service RpcService {
    Response Test(1:string input)
}
  1. Go與C的thrift代碼

thrift -r --gen go example.thrift
thrift -r --gen c_glib example.thrift

此時(shí)在目錄下會(huì)出現(xiàn)gen-go與gen-c_gib兩個(gè)文件夾,里邊存放著Thrift自動(dòng)生成的數(shù)據(jù)結(jié)構(gòu)體以及函數(shù)的聲明性雄。

3. Go的server端實(shí)現(xiàn)

/* server.go */

package main
import (
    "./gen-go/thrift/rpc"
    "git.apache.org/thrift.git/lib/go/thrift"
    "log"
    "os"
)

const (
    NetworkAddr = "localhost:9090"
)

type RpcServiceImpl struct {
}
func (this *RpcServiceImpl) Test(input string) (r *rpc.Response, err error) {
    //函數(shù)具體實(shí)現(xiàn)
    return
}

func main() {
    transportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
    
    protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
    
    serverTransport, err := thrift.NewTServerSocket(NetworkAddr)
    if err != nil {
        log.Println("Error!", err)
        os.Exit(1)
    }

    handler := &RpcServiceImpl{}
    processor := rpc.NewRpcServiceProcessor(handler)


    log.Println("thrift server in", NetworkAddr)
    server.Serve()
}

4. Go的客戶(hù)端實(shí)現(xiàn)

/* client.go */

import (
    "./gen-go/thrift/rpc"
    "fmt"
    "git.apache.org/thrift.git/lib/go/thrift"
    "net"
    "os"
)

func main(){

ip := "127.0.0.1"
port := "9090"
input :=""  

transportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
    protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()

    tSocket, err := thrift.NewTSocket(net.JoinHostPort(ip, port))
    if err != nil {
        fmt.Fprintln(os.Stderr, "Error resolving address, ", err)
        os.Exit(1)
    }

    tTransport, _ := transportFactory.GetTransport(tSocket)
    
    client := rpc.NewRpcServiceClientFactory(tTransport, protocolFactory)
    if err := tTransport.Open(); err != nil {
        fmt.Fprintln(os.Stderr, (fmt.Errorf("Error opening socket to %s:%s : %v", ip, port, err)))
        os.Exit(1)
    }
    defer tTransport.Close()

    resp, _ := client.Test(input)
}

C的客戶(hù)端實(shí)現(xiàn)

C 的客戶(hù)端實(shí)現(xiàn)目前在github一個(gè)都沒(méi)有没卸,Thrift好像也是最近才支持的。thrift是一種面向?qū)ο蟮目蚣苊胄珻語(yǔ)言面向?qū)ο蟮膶?shí)現(xiàn)必須依賴(lài)于gobject庫(kù)约计,所以這里邊在實(shí)現(xiàn)的過(guò)程中需要注意一點(diǎn),對(duì)thrift文件中定義的struct迁筛,其他可以直接實(shí)例化為對(duì)象煤蚌,在C中必須使用g_object_new函數(shù)進(jìn)行初始化,要不然改strcut 將無(wú)法實(shí)現(xiàn)瑰煎。在會(huì)一直出現(xiàn)無(wú)法找到對(duì)應(yīng)結(jié)構(gòu)接收server端傳來(lái)的參數(shù)铺然。

/* client.c */

#include <stdio.h>
#include <glib-object.h>
#include <string.h>

#include <thrift/c_glib/protocol/thrift_binary_protocol.h>
#include <thrift/c_glib/transport/thrift_framed_transport.h>
#include <thrift/c_glib/transport/thrift_socket.h>
#include "gen-c_glib/rpc_service.h"


struct thrift_if{  

   ThriftSocket *socket;
   ThriftTransport *transport;
   ThriftProtocol *protocol;
   RpcServiceIf *client;

};

void if_open (struct thrift_if* if_instance, gchar *hostname, gint32 port, GError **error){

#if (!GLIB_CHECK_VERSION (2, 36, 0))
    g_type_init ();
#endif

   if_instance->socket = g_object_new (THRIFT_TYPE_SOCKET,
                            "hostname",  hostname,
                            "port",      port,
                            NULL);

   if_instance->transport = g_object_new (THRIFT_TYPE_FRAMED_TRANSPORT,
                            "transport", if_instance->socket,
                            NULL);
    if_instance->protocol  = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL,
                            "transport", if_instance->transport,
                            NULL);
   
    thrift_transport_open (if_instance->transport, error);

    if(!error){
        return;
    }

    if_instance->client = g_object_new (TYPE_RPC_SERVICE_CLIENT,
                         "input_protocol",  if_instance->protocol,
                         "output_protocol", if_instance->protocol,
                         NULL);
   
}

void if_close (struct thrift_if *if_instance, GError **error){
   
    g_clear_error (error); 
    thrift_transport_close (if_instance->transport, NULL);
    g_object_unref (if_instance->client);
    g_object_unref (if_instance->protocol);
    g_object_unref (if_instance->transport);
    g_object_unref (if_instance->socket);
}

int main(){

    gchar *hostname = "127.0.0.1";
    gint32 port = 9090;
    gchar *input = ""
    
    struct thrift_if if_instance;
    GError *error = NULL; 
    if_open(&if_instance, hostname, port, &error);
    
    gchar *data;
    Response *Res;
    Res = g_object_new(TYPE_RESPONSE,NULL);
    
    if (!error && rpc_service_if_test(if_instance.client,&Res,input,&error)){   
            g_object_get (Res, "data", &data, NULL);
    }

   if_close(&if_instance, &error);

return 0;
} 

編譯:
gcc  client.c gen-c_glib/rpc_service.c gen-c_glib/sven_types.c -o client -lthrift_c_glib -lgobject-2.0

5. C代碼中調(diào)用Go的客戶(hù)端

由于C的客戶(hù)端編譯依賴(lài)于thrift_c_glib與 gobject 動(dòng)態(tài)庫(kù)俗孝,并且thrift_c_glib動(dòng)態(tài)庫(kù)中對(duì)linux的一些系統(tǒng)庫(kù)又進(jìn)行了引用酒甸,所以動(dòng)態(tài)庫(kù)的依賴(lài)關(guān)系復(fù)雜,不利于在客戶(hù)端穩(wěn)定赋铝、無(wú)依賴(lài)的部署插勤。
可以采用使用Go編寫(xiě)客戶(hù)端,然后編譯為.so文件,供C程序調(diào)用农尖。因?yàn)镚o采用靜態(tài)源碼編譯方式析恋,可以無(wú)依賴(lài)的移植到各個(gè)服務(wù)器中,在過(guò)程中需要注意C與Go基本數(shù)據(jù)結(jié)構(gòu)之間的轉(zhuǎn)化盛卡。代碼與go client的實(shí)現(xiàn)基本相同助隧,只是go 與 C直接不允許 struct的傳遞,所以只能傳遞基本數(shù)據(jù)類(lèi)型

/*client.go */
 
 package main

import (
    "./gen-go/thrift/rpc"
    "C"
    "fmt"
    "git.apache.org/thrift.git/lib/go/thrift"
    "net"
    "os"
)

/*  ;住2⒋濉!務(wù)必寫(xiě)上"http://export Test", 這不是注釋  W壹肌A埂!*/
//export Test
func Test (input string, ip string, port string) *C.char {

    transportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
    protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()

    tSocket, err := thrift.NewTSocket(net.JoinHostPort(ip, port))
    if err != nil {
        fmt.Fprintln(os.Stderr, "Error resolving address, ", err)
        os.Exit(1)
    }

    tTransport, _ := transportFactory.GetTransport(tSocket)
    client := rpc.NewRpcServiceClientFactory(tTransport, protocolFactory)
    if err := tTransport.Open(); err != nil {
        fmt.Fprintln(os.Stderr, (fmt.Errorf("Error opening socket to %s:%s : %v", ip, port, err)))
        os.Exit(1)
    }
    defer tTransport.Close()

    resp, _ := client.Test(input)

    return C.CString(resp.Data)
}

編譯為動(dòng)態(tài)庫(kù)令漂,執(zhí)行下面命令會(huì)生成libclient.h 與 libclient.so兩個(gè)文件膝昆。

go build -buildmode=c-shared -o libclient.so client.go

C 語(yǔ)言調(diào)用該 Go生成的動(dòng)態(tài)庫(kù):

#include <stdio.h>
#include "libclient.h"

int main()
{
    
    GoString input = {(char*)"test", 4};
    GoString ip = {(char*)"127.0.0.1", 9};
    GoString port = {(char*)"9090", 4};

    char *res = NULL;

    res = Test(input, ip, port);

    if (res != NULL)
    {
        printf("%s\n", res);
    }
    
    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市叠必,隨后出現(xiàn)的幾起案子荚孵,更是在濱河造成了極大的恐慌,老刑警劉巖纬朝,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件处窥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡玄组,警方通過(guò)查閱死者的電腦和手機(jī)滔驾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)俄讹,“玉大人哆致,你說(shuō)我怎么就攤上這事』继牛” “怎么了摊阀?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)踪蹬。 經(jīng)常有香客問(wèn)我胞此,道長(zhǎng),這世上最難降的妖魔是什么跃捣? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任漱牵,我火速辦了婚禮,結(jié)果婚禮上疚漆,老公的妹妹穿的比我還像新娘酣胀。我一直安慰自己刁赦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布闻镶。 她就那樣靜靜地躺著甚脉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铆农。 梳的紋絲不亂的頭發(fā)上牺氨,一...
    開(kāi)封第一講書(shū)人閱讀 50,021評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音墩剖,去河邊找鬼波闹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涛碑,可吹牛的內(nèi)容都是我干的精堕。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蒲障,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼歹篓!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起揉阎,我...
    開(kāi)封第一講書(shū)人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤庄撮,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后毙籽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體洞斯,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年坑赡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烙如。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡毅否,死狀恐怖亚铁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情螟加,我是刑警寧澤徘溢,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站捆探,受9級(jí)特大地震影響然爆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜黍图,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一曾雕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧雌隅,春花似錦翻默、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至检盼,卻和暖如春肯污,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吨枉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工蹦渣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人貌亭。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓柬唯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親圃庭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锄奢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

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