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ù)端躲舌。
- thrift 文件編寫(xiě)
#example.thrift
namespace go thrift.rpc
struct Response {
1: required string data;
}
service RpcService {
Response Test(1:string input)
}
- 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;
}