Thrift架構(gòu)簡介
Thrift自頂向下可分為四層
Server(single-threaded, event-driven)服務(wù)器進(jìn)程調(diào)度
Processor(compiler generated)RPC接口處理函數(shù)分發(fā),IDL定義接口的實(shí)現(xiàn)將掛接到這里面
-
Protocol (JSON, compact etc)協(xié)議,定義數(shù)據(jù)傳輸格式
- TBinaryProtocol(二進(jìn)制格式)
- TCompactProtocol(壓縮格式)
- TJSONProtocol (JSON格式)
- TDebugProtocol (易看的文本格式,方便debug)
-
Transport(raw TCP, HTTP etc)網(wǎng)絡(luò)傳輸,定義數(shù)據(jù)傳輸方式
- TSocket(阻塞式socket)
- TServerTransport(服務(wù)端模式,非阻塞socket)
- TFramedTransport(以幀為單位,非阻塞式)
- TMemoryTransport(內(nèi)存形式)
- TFileTransport(文件形式)
- TZlibTransport(使用zlib壓縮,與其他方式聯(lián)合使用)
Thrift實(shí)際上是實(shí)現(xiàn)了C/S模式,通過代碼生成工具將接口定義文件生成服務(wù)器端和客戶端代碼(可以為不同語言),從而實(shí)現(xiàn)服務(wù)端和客戶端跨語言的支持。
開發(fā)環(huán)境
系統(tǒng):macOS Big Sur 11.1
IDE :GoLand 2020.3.4
Thrift:0.14.1
軟件安裝
安裝thrift
brew install thrift # 安裝
thrift -version # 查看版本檢查是否安裝成功
安裝thrift support插件
Plugins->Marketplace搜索thrift support,安裝后重啟IDE即可
如果搜不到可以去官網(wǎng)下載對應(yīng)版本的安裝包本地安裝

開發(fā)
編寫thrift IDL
user.thrift
namespace go demo
struct User {
1:required i32 id,
2:required string name,
3:required string avatar,
4:required string address,
5:required string mobile,
}
struct UserList {
1:required list<User> userList,
2:required i32 page,
3:required i32 limit,
}
service.thrift
include "user.thrift"
// 標(biāo)記各語言的命名空間(包名),不同語言需要單獨(dú)聲明
namespace go demo
// 重新定義類型名稱,同c語言
typedef map<string, string> Data
// 定義響應(yīng)體結(jié)構(gòu)
struct Response {
1:required i32 errcode,
2:required string errmsg,
3:required Data data,
}
// 定義服務(wù)接口,相當(dāng)于go的interface
service Greeter {
Response SayHello(
1:required user.User user
)
Response GetUser(
1:required i32 uid
)
}
生成目標(biāo)語言代碼
執(zhí)行命令:thrift -r --gen go service.thrift
生成以下代碼文件:

編寫golang服務(wù)端代碼
服務(wù)端:
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"github.com/apache/thrift/lib/go/thrift"
"os"
"thrift_practice/src/gen-go/demo"
)
func Usage() {
fmt.Fprint(os.Stderr, "Usage of ", os.Args[0], ":\n")
flag.PrintDefaults()
fmt.Fprint(os.Stderr, "\n")
}
//定義服務(wù)
type Greeter struct {
}
//實(shí)現(xiàn)IDL里定義的接口
//SayHello
func (this *Greeter) SayHello(ctx context.Context, u *demo.User) (r *demo.Response, err error) {
strJson, _ := json.Marshal(u)
return &demo.Response{Errcode: 0, Errmsg: "success", Data: map[string]string{"User": string(strJson)}}, nil
}
//GetUser
func (this *Greeter) GetUser(ctx context.Context, uid int32) (r *demo.Response, err error) {
return &demo.Response{Errcode: 1, Errmsg: "user not exist."}, nil
}
func main() {
//命令行參數(shù)
flag.Usage = Usage
addr := flag.String("addr", "localhost:9090", "Address to listen to")
flag.Parse()
//protocol
var protocolFactory thrift.TProtocolFactory
protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
//transport
var transportFactory thrift.TTransportFactory
transportFactory = thrift.NewTTransportFactory()
//handler
handler := &Greeter{}
//transport,no secure
var err error
var transport thrift.TServerTransport
transport, err = thrift.NewTServerSocket(*addr)
if err != nil {
fmt.Println("error running server:", err)
}
//processor
processor := demo.NewGreeterProcessor(handler)
fmt.Println("Starting the simple server... on ", *addr)
//start tcp server
server := thrift.NewTSimpleServer4(processor, transport, transportFactory, protocolFactory)
err = server.Serve()
if err != nil {
fmt.Println("error running server:", err)
}
}
客戶端:(借助go testing)
package main
import (
"context"
"fmt"
"github.com/apache/thrift/lib/go/thrift"
"testing"
"thrift_practice/src/gen-go/demo"
)
var ctx = context.Background()
func GetClient() *demo.GreeterClient {
addr := ":9090"
var transport thrift.TTransport
var err error
transport, err = thrift.NewTSocket(addr)
if err != nil {
fmt.Println("Error opening socket:", err)
}
//protocol
var protocolFactory thrift.TProtocolFactory
protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
//no buffered
var transportFactory thrift.TTransportFactory
transportFactory = thrift.NewTTransportFactory()
transport, err = transportFactory.GetTransport(transport)
if err != nil {
fmt.Println("error running client:", err)
}
if err := transport.Open(); err != nil {
fmt.Println("error running client:", err)
}
iprot := protocolFactory.GetProtocol(transport)
oprot := protocolFactory.GetProtocol(transport)
client := demo.NewGreeterClient(thrift.NewTStandardClient(iprot, oprot))
return client
}
//GetUser
func TestGetUser(t *testing.T) {
client := GetClient()
rep, err := client.GetUser(ctx, 100)
if err != nil {
t.Errorf("thrift err: %v\n", err)
} else {
t.Logf("Recevied: %v\n", rep)
}
}
//SayHello
func TestSayHello(t *testing.T) {
client := GetClient()
user := &demo.User{}
user.Name = "thrift"
user.Address = "address"
rep, err := client.SayHello(ctx, user)
if err != nil {
t.Errorf("thrift err: %v\n", err)
} else {
t.Logf("Recevied: %v\n", rep)
}
}
運(yùn)行測試
- 運(yùn)行服務(wù)端代碼
- 運(yùn)行客戶端:
go test -v
參考資料
【1】從零開始基于go-thrift創(chuàng)建一個(gè)RPC服務(wù)
【2】Go Tutorial
【3】Thrift RPC框架指南
