Fork me on GitHub

【协议】Thrift的TJsonProtocol协议分析

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.

posted @ 2016-02-01 15:00  Mr.YF  阅读(5244)  评论(0编辑  收藏  举报