Thrift的TJsonProtocol协议分析

摘要:
节俭协议实现目前包括二进制协议、紧凑二进制协议和Json协议。前两篇文章从编码和协议原理方面分析了TBinaryProtocol和TCompartProtocol协议。接下来,我们将分析TJsonProtocol协议。TJsonProtocol相对简单,在网络中以文本模式传输,易于捕获、分析和理解。

Thrift协议实现目前有二进制协议(TBinaryProtocol),紧凑型二进制协议(TCompactProtocol)和Json协议(TJsonProtocol)。

前面的两篇文字从编码和协议原理方面分析了TBinaryProtocol和TCompactProtocol协议,下面对TJsonProtocol协议做一下分析。

TJsonProtocol协议相对比较简单,在网络中以文本方式传输,易于抓包分析和理解。

1. 数据类型表示方式和简写

数据类型
数据类型Json协议节点简写C++表示方式Go表示方式Java表示方式Lua表示方式
布尔tfboolboolbooleantrue/false
字节/8位i8int8_t,charint8byte 
16位整数i16int16_tint16short 
32位整数i32int32_tint32int 
64位整数i64int64_tint64long 
双精度小数dbldoublefloat64double 
字符串strstringstringString 
结构体recstructstructclasstable
列表lststd:list<value>[]typeList<ValueType>table[value] = bool
集合setstd:set<value>map[ValueType]boolSet<ValueType>table[value] = bool
字典/映射mapstd:map<key, value>map[KeyType]ValueTypeMap<KeyType,ValueType>table[key] = value

2.各数据类型表示方式

Thrift的TJsonProtocol协议分析第1张

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.

免责声明:文章转载自《Thrift的TJsonProtocol协议分析》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇elasticsearch的服务器响应异常及解决策略(转)使用注册表来记录文件夹对话框的打开路径下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

XML文件与实体类的互相转换

一.将XML文件反序列化为实体类对象   1. 通常程序的配置信息都保存在程序或者网站的专门的配置文件中(App.config/web.config)。但是现在为了演示XML序列化和反序列化,将配置信息保存在一个XML文件(config.xml)中,通过反序列化将配置信息读取出来保存到一个单独的类(Config.cs)中。这样如果需要用到配置信息,没必要每...

java序列化/反序列化之xml、protobuf、protostuff 的比较与使用例子

目录 1、背景 2、测试   2.1、环境   2.2、工具   2.3、说明   2.4、结果   2.5、结论 3、xml简单教程   3.1、准备   3.2、代码  4、protobuf简单教程   4.1、快速入门     1、下载.exe编译器     2、编写.proto文件     3、利用编译器编译.proto文件生成javabean  ...

Java接口自动化测试实战001----get、post方法实现与封装

一、接口测试 1、接口测试流程 根据接口文档编写测试用例 准备测试数据 准备工具(测试工具or接口测试代码) 填写接口信息(接口地址、请求方式等) 准备请求头数据(如果有必要,比如:cookies,Content-Type等) 发起请求,获取接口的相应信息(状态码、响应报文、或者某些特殊的响应头数据) 根据报文判断实际与预期结果是否一致 2、HTTP请...

Mac系统修改Intellij Idea默认JDK版本

Intellij IDEA 默认情况下,使用的jdk的版本是1.6,当第一次启动IDEA的时候,如果系统中未安装jdk,则系统会自动到苹果官网下载jdk安装文件。如果你的系统已经安装了jdk1.7或是更高的版本,同样首次打开IDEA的时候要求你安装苹果官网jdk1.6。 为了免去多余的jdk安装,解决办法如下: 到/Applications下找到Intel...

Java学习:JDBC各类详解

 JDBC各个类详解 代码实现: //1.导入驱动jar包 //2.注册驱动 Class.forName("com.mysql.jdbc.Driver"); //3.获取数据库连对象 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3","root",...

Java消息系统简单设计与实现

前言:由于导师在我的毕设项目里加了消息系统(本来想水水就过的..),没办法...来稍微研究研究吧..简单简单... 需求分析 我的毕设是一个博客系统,类似于简书这样的,所以消息系统也类似,在用户的消息里包含了有:喜欢和赞、评论、关注、私信这样的一类东西,这样的一个系统应该包含以下的功能: 当用户评论/关注/点赞时能够通知到被评论/关注/点赞的用户,并...