1. 程式人生 > >Thrift 的C與golang語言實現以及相互呼叫方式

Thrift 的C與golang語言實現以及相互呼叫方式

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.thrift
thrift -r --gen c_glib example.thrift

此時在目錄下會出現gen-go與gen-c_gib兩個資料夾,裡邊存放著Thrift自動生成的資料結構體以及函式的宣告。

3. Go的server端實現

/* 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) {
    //函式具體實現
    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的傳遞,所以只能傳遞基本資料型別

/*client.go */

 package main

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

/*  !!!務必寫上"//export Test", 這不是註釋  !!!*/
//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)
}

編譯為動態庫,執行下面命令會生成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;
}

相關推薦

ThriftCgolang語言實現以及相互呼叫方式

Thrift 的Go與C語言實現 Thrift 是Facebook為了解決各系統間大資料量的傳輸通訊以及系統之間語言環境不同而設計的一種傳輸框架。目前來看常用的主流語言Thrift都已經很好地支援,並且github上已經有很多實現,除了C語言之外。T

使用SWIG實現C/C++其他語言間的互相呼叫

  為了執行速度,java, C#, ruby等語言可能會呼叫C/C++寫的函式或者類,但直接寫很麻煩,必須遵循各種語言訂立的規範,以jni為例要匯出一個函式給java用,你必須在函式定義前寫上JNIEXPORT,對於那些早已存在的程式碼,這樣就麻煩了,而且相同的程式碼如果要

C++Lua5.3.2的相互呼叫

重Lua官網下載最新的Lua5.3.2解壓後把src檔案下的所有檔案(Lua.c,Luac.c除外)複製到專案的目錄下,並新增到專案中, 建立一個Lua指令碼檔案 --region *.lua --Date --此檔案由[BabeLua]外掛自動生成 print("lu

資料結構之連結串列C語言實現以及使用場景分析

連結串列是資料結構中比較基礎也是比較重要的型別之一,那麼有了陣列,為什麼我們還需要連結串列呢!或者說設計連結串列這種資料結構的初衷在哪裡? 這是因為,在我們使用陣列的時候,需要預先設定目標群體的個數,也即陣列容量的大小,然而實時情況下我們目標的個數我們是不確定的,因此我們總是要把陣列的容量設定的

AVL樹原理及實現C語言實現以及Java語言實現

歡迎探討,如有錯誤敬請指正 如需轉載,請註明出處http://www.cnblogs.com/nullzx/ 1. AVL定義 AVL樹是一種改進版的搜尋二叉樹。對於一般的搜尋二叉樹而言,如果資料恰好是按照從小到大的順序或者從大到小的順序插入的,那麼搜尋二叉樹就對退化成連結串列,這個時候查詢,插入和刪除的

連結串列的c語言實現以及根據linux核心中連結串列的實現過程

轉自 : http://blog.csdn.net/lickylin/article/details/8010618 連結串列,就是用一組任意的儲存單元儲存線性表元素的一種資料結構。連結串列又分為單鏈表、雙向連結串列和迴圈連結串列等。 下面程式碼是連結串列的兩種實現方式

C++ Java 語言對比

1 . Java 是完全封裝的,而 C++ 的函式是可以定義在 Class 的外部的。從這裡就可以看出 C++ 的 OO 思想不夠徹底,至少在封裝這一點上。 2. C++ 中有拷貝

php的一些理解(物件類的概念以及相互關係理解)

先來看一段簡單的程式碼: <?php class Person{ public $name; public $age; public $sex; public function who() { echo $this->

Linux下用c語言實現傳送http請求 方式可以Get或者Post例程參考

[1].[程式碼] Linux下用c語言實現傳送http請求 方式可以Get或者Post 跳至 [1] ? 1 2

C#matlab混合程式設計以及C#程式設計

把最近所做的C#與MATLAB混合程式設計,還有介面一些問題進行總結。MATLAB有非常強大的運算功能,C#有很多封裝好的庫可以用來做介面,所以利用兩者的優勢來製作一個c#呼叫MATLAB演算法程式的展示介面。 C#與MATLAB混合程式設計是整個專案中比較難的一部分,主要

【LeetCode】 ZigZag Conversion 【Python || C++】雙語言實現

題目(來自leetcode網站): 實現了python版本 實現了C++版本 題目含義為,將輸入字串 首先根據numRows大小進行 列的 Z 字形 排列 後,把每一行直接拼接起來輸出; The string "PAYPALISHIRING" is written i

【LeetCode】Longest Palindromic Substring【Python || C++】雙語言實現

題目(來自leetcode網站): 題目的意思是把 輸入的整數倒敘輸出,不改變符號,且需要對整數的範圍進行判斷; Given a 32-bit signed integer, reverse dig

Java多執行緒之Callable介面Runnable的實現以及選擇

通過實現Runnable介面的實現 package Thread; import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; public class RunnableThreadDemo { pr

應用統計學R語言實現學習筆記(五)——引數估計

Chapter 5 Estimation 本篇是第五章,內容是引數估計。 1.引數估計的一般問題 正如前面介紹的,統計學的兩大分支,分別是描述統計和推斷統計。所以今天來談談推斷統計的第一大問題——引數估計。當然一般叫統計推斷的會更多些,二者是一樣

應用統計學R語言實現學習筆記(二)——資料收集

Chapter 2 Data Collection 本篇是第二章,內容是資料收集。 1.資料來源 做科學研究離不開資料,而資料的來源有哪些呢? 這裡比較簡單地將資料來源分為兩類:直接(一手)資料和間接(二手)資料。 直接資料的資料獲取來源包括

【決策樹】ID3演算法理解R語言實現

一、演算法理解 想來想去,還是決定用各大暢銷書中的相親例子來解釋什麼叫決策樹。 簡單來說,決策樹就是根據各種變數,作為輸入條件,最終輸出決策的過程。比如上圖中女方在相親過程中,影響是否見男方的變數有年齡、長相、收入、是否是公務員等。 最終在各種變數組合下,最終輸出見或不

應用統計學R語言實現學習筆記後記

1 後記 應用統計學與R語言實現學習筆記這一系列部落格斷斷續續寫了5個月左右。現在終於算是基本完成了。我個人比較強迫症,比較喜歡一個系列更完再更其他的。所以中間有一些不錯的內容想寫到部落格裡都沒動筆。後面會繼續填坑。另外之後遇到的跟應用統計學與R語言實現相關的

決策樹ID3;C4.5詳解和python實現R語言實現比較

本文網址:http://blog.csdn.net/crystal_tyan/article/details/42130851(請不要在採集站閱讀) 把決策樹研究一下,找來了一些自己覺得還可以的資料: 分類樹(決策樹)是一種十分常用的分類方法。他是一種監管學習,所謂監管

應用統計學R語言實現學習筆記(十一)——判別分析

Chapter 11 Discriminant Analysis 筆者最近任務繁重,斷更了一頓時間,最近會開始慢慢把這個系列寫完。本篇是第十一章,內容是判別分析。 1 判別分析應用 判別分析(Discriminant Analysis)——判別分

MFC中CStringstring的區別以及相互轉換關係

區別: CString 類是微軟的visual c++提供的MFC裡面的一個類,所以只有支援MFC的工程才可以使用。如在linux上的工程就不能用CString了,只能用標準C++中的 string類了。另外,因為string類是在c++標準庫中,所以它被封裝在了std名稱