Thrift 框架主要用于跨語言服務(wù)的開發(fā)。一般場景下,服務(wù)端對于接口的互相調(diào)用可以直接使用 http 請求來完成,這種方式簡單并且方便,但是,數(shù)據(jù)的傳輸速度并不是很快,并且很難規(guī)范出一種統(tǒng)一的調(diào)用方式。
比較理想的調(diào)用方式應(yīng)該是這樣,對于客戶端來說,服務(wù)端的實(shí)現(xiàn)方式是透明的,客戶端只需要知道自己可以調(diào)用哪些方法,以及如何調(diào)用這些方法,所謂如何調(diào)用,是指相關(guān)方法需要傳入什么參數(shù),以及其返回值格式。在thrift框架中,這種約定可以通過接口描述來實(shí)現(xiàn)。
在確定接口描述之后,實(shí)際上對于任何需要與服務(wù)端通信的客戶端來說,調(diào)用方式就已經(jīng)確定。而客戶端可以由其它各種語言自己實(shí)現(xiàn)。
數(shù)據(jù)類型
基礎(chǔ)類型
- bool: A boolean value (true or false)
- byte: An 8-bit signed integer
- i16: A 16-bit signed integer
- i32: A 32-bit signed integer
- i64: A 64-bit signed integer
- double: A 64-bit floating point number
- string: A text string encoded using UTF-8 encoding
結(jié)構(gòu)體
通其它語言中一樣,IDL中的結(jié)構(gòu)體,定義一種數(shù)據(jù)類型的集合,這個變量可以在其他地方被使用。對應(yīng)于具體的語言,類似于類的概念。
容器
在IDL中,這種存放數(shù)據(jù)的結(jié)構(gòu)稱為容器,在編譯之后的具體語言文件中,將會對應(yīng)到這種語言所支持的數(shù)據(jù)結(jié)構(gòu)。
- list: An ordered list of elements. Translates to an STL vector, Java ArrayList, native arrays in scripting languages, etc.
- set: An unordered set of unique elements. Translates to an STL set, Java HashSet, set in Python, etc. Note: PHP does not support sets, so it is treated similar to a List
- map: A map of strictly unique keys to values. Translates to an STL map, Java HashMap, PHP associative array, Python/Ruby dictionary, etc. While defaults are provided, the type mappings are not explicitly fixed. Custom code generator directives have been added to allow substitution of custom types in various destination languages.
Services
定義 service 相當(dāng)于定義接口,或者純虛抽象類,定義的方法相當(dāng)于面向?qū)ο笳Z言中的純虛函數(shù)。thrift 的編譯器將根據(jù)相關(guān)定義來實(shí)現(xiàn)客戶端和服務(wù)端的代碼。
service 由一組函數(shù)組成,每個函數(shù)都有一組參數(shù)定義和返回值的定義。
IDL
以官網(wǎng) tutorial.thrift 示例來做說明
常量定義
// 定義32位整型
const i32 INT32CONSTANT = 9853
//定義一個map結(jié)構(gòu)
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}
//定義一個結(jié)構(gòu)體,每個字段前面都有一個整數(shù)作為標(biāo)記,字段可設(shè)置默認(rèn)值
struct Work {
1: i32 num1 = 0,
2: i32 num2,
3: Operation op,
4: optional string comment,
}
//定義一個service
service Calculator extends shared.SharedService {
void ping(),
//返回值,參數(shù)列表同樣用數(shù)字作為標(biāo)記
i32 add(1:i32 num1, 2:i32 num2),
i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),
/**
* This method has a oneway modifier. That means the client only makes
* a request and does not listen for any response at all. Oneway methods
* must be void.
*/
oneway void zip()
}
編譯
可直接在本地安裝 thrift 編譯器,或者用docker來運(yùn)行編譯命令。docker 運(yùn)行可能更方便一些,還可以避免本機(jī)環(huán)境和部署環(huán)境的不同而導(dǎo)致代碼無法運(yùn)行。
運(yùn)行編譯命令:thrift --gen <language> <Thrift filename>
以 go 語言為例:thrift --gen go tutorial.thrift
編譯過程中,會生成 go-gen 目錄,編譯好的包也會自動放入這個目錄。上述 thrift 文件則生成了包名為 tutorial 的 go 語言包。然后將生成的包移動到 GOPATH 目錄下,就可以直接調(diào)用了。
運(yùn)行
以官網(wǎng)示例作為說明:
https://thrift.apache.org/tutorial/go
代碼分為三部分,server handler client。
server.go 啟動了一個服務(wù)端程序,handler.go 則具體實(shí)現(xiàn)了service里面定義的純虛函數(shù),client.go 則是遠(yuǎn)程調(diào)用的實(shí)現(xiàn)方式。
運(yùn)行 server.go 需要向其添加入口函數(shù),完整代碼如下:
package main
import (
"crypto/tls"
"fmt"
"tutorial"
"./handler"
"git.apache.org/thrift.git/lib/go/thrift"
)
func runServer(transportFactory thrift.TTransportFactory, protocolFactory thrift.TProtocolFactory, addr string, secure bool) error {
var transport thrift.TServerTransport
var err error
if secure {
cfg := new(tls.Config)
if cert, err := tls.LoadX509KeyPair("server.crt", "server.key"); err == nil {
cfg.Certificates = append(cfg.Certificates, cert)
} else {
return err
}
transport, err = thrift.NewTSSLServerSocket(addr, cfg)
} else {
transport, err = thrift.NewTServerSocket(addr)
}
if err != nil {
return err
}
fmt.Printf("%T\n", transport)
//引入 handler 并傳入,此處相當(dāng)于返回一個對象實(shí)例,對象的方法就是純虛函數(shù)的具體實(shí)現(xiàn)
handler := handler.NewCalculatorHandler()
processor := tutorial.NewCalculatorProcessor(handler)
server := thrift.NewTSimpleServer4(processor, transport, transportFactory, protocolFactory)
fmt.Println("Starting the simple server... on ", addr)
return server.Serve()
}
func main() {
transportFactory := thrift.NewTBufferedTransportFactory(1024)
protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
//監(jiān)聽 1234 端口
addr := "127.0.0.1:1234"
runServer(transportFactory, protocolFactory, addr, false)
}
運(yùn)行 client.go 同樣需要添加入口函數(shù) main ,并初始化連接,步驟和上述相同。