Thrift 的C與golang語言實現以及相互調用方式

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

  • Thrift 的Go與C語言實現
    • thrift 檔案編寫
    • Go與C的thrift代碼
    • Go的server端實現
    • Go的用戶端實現
    • C的用戶端實現
    • C代碼中調用Go的用戶端

Thrift 的Go與C語言實現

Thrift 是Facebook為瞭解決各系統間大資料量的傳輸通訊以及系統之間語言環境不同而設計的一種傳輸架構。目前來看常用的主流語言Thrift都已經很好地支援,並且github上已經有很多實現,除了C語言之外。Thrift傳輸的程式的待用資料,即資料的資料結構必須事前固定。Thrift原理就不介紹了,理論性東西網上很多,並且都是雷同的。下面通過執行個體介紹Thrift 介面在Go與C語言下的實現,以及如何在C語言中調用Go所編寫的Thrift用戶端。

1. thrift 檔案編寫

#example.thrift    namespace go thrift.rpc    struct Response {    1: required string data;    }service RpcService {    Response Test(1:string input)}

2. Go與C的thrift代碼

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

此時在目錄下會出現gen-go與gen-c_gib兩個檔案夾,裡邊存放著Thrift自動產生的資料結構體以及函數的聲明。

3. Go的server端實現

/* server.go */package mainimport (    "./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) {    //函數具體實現    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的用戶端實現

/* 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的用戶端實現

C 的用戶端實現目前在github一個都沒有,Thrift好像也是最近才支援的。thrift是一種物件導向的架構,C語言物件導向的實現必須依賴於gobject庫,所以這裡邊在實現的過程中需要注意一點,對thrift檔案中定義的struct,其他可以直接執行個體化為對象,在C中必須使用g_object_new函數進行初始化,要不然改strcut 將無法實現。在會一直出現無法找到對應結構接收server端傳來的參數。

/* 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代碼中調用Go的用戶端

由於C的用戶端編譯依賴於thrift_c_glib與 gobject 動態庫,並且thrift_c_glib動態庫中對linux的一些系統庫又進行了引用,所以動態庫的依賴關係複雜,不利於在用戶端穩定、無依賴的部署。
可以採用使用Go編寫用戶端,然後編譯為.so檔案,供C程式調用。因為Go採用靜態源碼編譯方式,可以無依賴的移植到各個伺服器中,在過程中需要注意C與Go基本資料結構之間的轉化。代碼與go client的實現基本相同,只是go 與 C直接不允許 struct的傳遞,所以只能傳遞基礎資料型別 (Elementary Data Type)

/*client.go */ package mainimport (    "./gen-go/thrift/rpc"    "C"    "fmt"    "git.apache.org/thrift.git/lib/go/thrift"    "net"    "os")/*  !!!務必寫上"//export Test", 這不是注釋  !!!*///export Testfunc 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)}

編譯為動態庫,執行下面命令會產生libclient.h 與 libclient.so兩個檔案。

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

C 語言調用該 Go產生的動態庫:

#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;}
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.