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)
}
  1. 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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容