Thrift協議實現目前有二進制協議(TBinaryProtocol),緊湊型二進制協議(TCompactProtocol)和Json協議(TJsonProtocol)。
前面的兩篇文字從編碼和協議原理方面分析了TBinaryProtocol和TCompactProtocol協議,下面對TJsonProtocol協議做一下分析。
TJsonProtocol協議相對比較簡單,在網絡中以文本方式傳輸,易於轉包分析和理解。
1. 數據類型表示方式和簡寫
數據類型 | Json協議節點簡寫 | C++表示方式 | Go表示方式 | Java表示方式 | Lua表示方式 |
布爾 | tf | bool | bool | boolean | true/false |
字節/8位 | i8 | int8_t,char | int8 | byte | |
16位整數 | i16 | int16_t | int16 | short | |
32位整數 | i32 | int32_t | int32 | int | |
64位整數 | i64 | int64_t | int64 | long | |
雙精度小數 | dbl | double | float64 | double | |
字符串 | str | string | string | String | |
結構體 | rec | struct | struct | class | table |
列表 | lst | std:list<value> | []type | List<ValueType> | table[value] = bool |
集合 | set | std:set<value> | map[ValueType]bool | Set<ValueType> | table[value] = bool |
字典/映射 | map | std:map<key, value> | map[KeyType]ValueType | Map<KeyType,ValueType> | table[key] = value |
2.各數據類型表示方式
bool,i8,i16,i32,i64,double,string的Json表示格式:
"編號": {
"類型": "值"
},
結構體Json表示格式:
"編號": {
"rec": {
"成員編號": {
"成員類型": "成員值"
},
...
}
}
Map的Json表示格式:
"編號": {
"map": ["鍵類型",
"值類型",
元素個數,
{
"鍵1": "值1",
"鍵n": "值n"
}
]
},
Set和List的Json表示方式:
"編號": {
"set/lst": ["值類型",
元素個數,
"ele1",
"ele2",
"elen"
]
},
3. 編寫Thrift的IDL文件並生成Golang代碼
thrift --gen go rpc.thrift
namespace go demo.rpc
namespace cpp demo.rpc
namespace java demo.rpc
struct ArgStruct {
1:byte argByte,
2:string argString
3:i16 argI16,
4:i32 argI32,
5:i64 argI64,
6:double argDouble,
7:bool argBool,
}
service RpcService {
list<string> funCall(
1:ArgStruct argStruct,
2:byte argByte,
3:i16 argI16,
4:i32 argI32,
5:i64 argI64,
6:double argDouble,
7:string argString,
8:map<string, string> paramMapStrStr,
9:map<i32, string> paramMapI32Str,
10:set<string> paramSetStr,
11:set<i64> paramSetI64,
12:list<string> paramListStr,
13:bool argBool,
),
}
編寫客戶端測試代碼
package main
import (
"demo/rpc"
"fmt"
"git.apache.org/thrift.git/lib/go/thrift"
"net"
"os"
"time"
)
func main() {
startTime := currentTimeMillis()
transportFactory := thrift.NewTTransportFactory()
protocolFactory := thrift.NewTJSONProtocolFactory()
transport, err := thrift.NewTSocket(net.JoinHostPort("10.10.36.143", "8090"))
if err != nil {
fmt.Fprintln(os.Stderr, "error resolving address:", err)
os.Exit(1)
}
useTransport := transportFactory.GetTransport(transport)
client := rpc.NewRpcServiceClientFactory(useTransport, protocolFactory)
if err := transport.Open(); err != nil {
fmt.Fprintln(os.Stderr, "Error opening socket to 127.0.0.1:19090", " ", err)
os.Exit(1)
}
defer transport.Close()
for i := 0; i < 1000; i++ {
argStruct := &rpc.ArgStruct{}
argStruct.ArgByte = 53
argStruct.ArgString = "str value"
argStruct.ArgI16 = 54
argStruct.ArgI32 = 12
argStruct.ArgI64 = 43
argStruct.ArgDouble = 11.22
argStruct.ArgBool = true
paramMap := make(map[string]string)
paramMap["name"] = "namess"
paramMap["pass"] = "vpass"
paramMapI32Str := make(map[int32]string)
paramMapI32Str[10] = "val10"
paramMapI32Str[20] = "val20"
paramSetStr := make(map[string]bool)
paramSetStr["ele1"] = true
paramSetStr["ele2"] = true
paramSetStr["ele3"] = true
paramSetI64 := make(map[int64]bool)
paramSetI64[11] = true
paramSetI64[22] = true
paramSetI64[33] = true
paramListStr := []string{"l1.","l2."}
r1, e1 := client.FunCall(argStruct,
53, 54, 12, 34, 11.22, "login", paramMap,paramMapI32Str,
paramSetStr, paramSetI64, paramListStr, false)
fmt.Println(i, "Call->", r1, e1)
break
}
endTime := currentTimeMillis()
fmt.Println("Program exit. time->", endTime, startTime, (endTime - startTime))
}
// 轉換成毫秒
func currentTimeMillis() int64 {
return time.Now().UnixNano() / 1000000
}
使用NewTJSONProtocolFactory方法使用Json協議。
編寫服務段測試代碼
package main
import (
"demo/rpc"
"fmt"
"git.apache.org/thrift.git/lib/go/thrift"
"os"
)
const (
NetworkAddr = ":8090"
)
type RpcServiceImpl struct {
}
func (this *RpcServiceImpl) FunCall(argStruct *rpc.ArgStruct,
argByte int8, argI16 int16, argI32 int32,
argI64 int64, argDouble float64, argString string,
paramMapStrStr map[string]string, paramMapI32Str map[int32]string,
paramSetStr map[string]bool, paramSetI64 map[int64]bool,
paramListStr []string, argBool bool) (r []string, err error) {
fmt.Println("-->FunCall:", argStruct)
r = append(r, "return 1 by FunCall.")
r = append(r, "return 2 by FunCall.")
return
}
func main() {
transportFactory := thrift.NewTTransportFactory()
protocolFactory := thrift.NewTJSONProtocolFactory()
serverTransport, err := thrift.NewTServerSocket(NetworkAddr)
if err != nil {
fmt.Println("Error!", err)
os.Exit(1)
}
handler := &RpcServiceImpl{}
processor := rpc.NewRpcServiceProcessor(handler)
server := thrift.NewTSimpleServer4(processor, serverTransport,transportFactory, protocolFactory)
fmt.Println("thrift server in", NetworkAddr)
server.Serve()
}
使用NewTJSONProtocolFactory方法使用Json協議。
測試前抓包分析
請求報文:
[
1,
"funCall",
1,
1,
{
"1": {
"rec": {
"1": {
"i8": 53
},
"2": {
"str": "str value"
},
"3": {
"i16": 54
},
"4": {
"i32": 12
},
"5": {
"i64": 43
},
"6": {
"dbl": 11.22
},
"7": {
"tf": 1
}
}
},
"2": {
"i8": 53
},
"3": {
"i16": 54
},
"4": {
"i32": 12
},
"5": {
"i64": 34
},
"6": {
"dbl": 11.22
},
"7": {
"str": "login"
},
"8": {
"map": [
"str",
"str",
2,
{
"name": "namess",
"pass": "vpass"
}
]
},
"9": {
"map": [
"i32",
"str",
2,
{
"10": "val10",
"20": "val20"
}
]
},
"10": {
"set": [
"str",
3,
"ele1",
"ele2",
"ele3"
]
},
"11": {
"set": [
"i64",
3,
11,
22,
33
]
},
"12": {
"lst": [
"str",
2,
"l1.",
"l2."
]
},
"13": {
"tf": 0
}
}
]
請求報文分析:
一條消息用中括號 [] 括起來。
第1個元素1 表示協議版本,目前TJsonProtocol協議版本爲1。
第2個元素funCall 表示消息的名稱。
第3個元素1 表示消息請求,(消息請求:1,消息響應:2,消息異常:3,oneway消息:4)。
第4個元素1 表示消息流水號。
一條消息的參數用大括號{} 括起來。
消息參數的node鍵名稱爲thrift文件中定義的字段編號,node值由值類型和值組成。
如:
"1": {
"i8": 53
},
1 表示字段編號,i8表示值類型爲8位整數或一個字節,53表示值。
其他也是同樣的含義,不再贅述。
響應報文:
[
1,
"funCall",
2,
1,
{
"0": {
"lst": [
"str",
2,
"return 1 by FunCall.",
"return 2 by FunCall."
]
}
}
]
響應報文分析:
一條消息用中括號 [] 括起來。
第1個元素1 表示協議版本,目前TJsonProtocol協議版本爲1。
第2個元素funCall 表示消息的名稱。
第3個元素2 表示消息響應,(消息請求:1,消息響應:2,消息異常:3,oneway消息:4)。
第4個元素1 表示消息流水號。
接下來的字段是返回的參數。
Done.