IT评测·应用市场-qidao123.com

标题: tunm二进制协议在python上的实现 [打印本页]

作者: 农民    时间: 2022-8-11 22:08
标题: tunm二进制协议在python上的实现
tunm二进制协议在python上的实现

tunm是一种对标JSON的二进制协议, 支持JSON的所有类型的动态组合
支持的数据类型

基本支持的类型 "u8",   "i8",   "u16",   "i16",   "u32",   "i32", "u64",   "i64", "varint", "float",   "string",  "raw", "array",  "map"
为什么我们需要二进制协议

下图是文本格式JSON与tunm的对比
类型可读可编辑编码速度解码速度数据大小预定义JSON✓✓慢慢大否tunmxx快快小否protobufxx快快小是在高性能的场景下, 或者需要流量传输比较敏感的地方, 通常会选择二进制来代替文本协议来做为通讯的, 如RPC, REST, 游戏等情况。
相对于google protobuf, 它需要比较完善的预定义过程, 就比如客户端版本1, 服务端版本2, 就有比较大的可能造成不兼容, 对需求经常变化的就会比较难与同步。
tunm相对于JSON, 若第一版是
  1. {
  2.     "name": "tunm", "version": 1
  3. }
复制代码
此时第二版需要加入用户的id, 就可以很方便的变成
  1. {
  2.     "name": "tunm", "version": 2, "id": 1
  3. }
复制代码
而对客户端1来说, 只是多一个id的字段, 不会有任何的破坏, 做到版本升级而无影响
协议的二进制格式

数据协议分为三部分(协议名称, 字符串索引区, 数据区(默认为数组))
如数据协议名为cmd_test_op, 数据为["tunm_proto", {"name": "tunm_proto", "tunm_proto": 1}]
协议的实现(小端对齐)

ByteBuffer的实现

ByteBuffer具有组装字节流的功能, 比如写入字符串, 写入int, 还有里面存储字符串索引区
  1. class ByteBuffer(object):
  2.     def __init__(self):
  3.         # 字节缓冲区
  4.         self.buffer = bytearray([00]*1024)
  5.         # 写入的位置索引号
  6.         self.wpos = 0
  7.         # 读出的位置索引号
  8.         self.rpos = 0
  9.         # 大小端格式
  10.         self.endianness = "little"
  11.         # 索引的数组及快速查询的字符串索引号
  12.         self.str_arr = []
  13.         self.str_map = {}
复制代码
ByteBuffer源码地址
类型的定义
  1. @enum.unique
  2. class TP_DATA_TYPE(IntEnum):
  3.     TYPE_NIL = 0,
  4.     TYPE_BOOL = 1,
  5.     TYPE_U8 = 2,
  6.     TYPE_I8 = 3,
  7.     TYPE_U16 = 4,
  8.     TYPE_I16 = 5,
  9.     TYPE_U32 = 6,
  10.     TYPE_I32 = 7,
  11.     TYPE_U64 = 8,
  12.     TYPE_I64 = 9,
  13.     TYPE_VARINT = 10,
  14.     TYPE_FLOAT = 11,
  15.     TYPE_DOUBLE = 12,
  16.     TYPE_STR = 13,
  17.     TYPE_STR_IDX = 14,
  18.     TYPE_RAW = 15,
  19.     TYPE_ARR = 16,
  20.     TYPE_MAP = 17,
复制代码
数据的组装

变长的int类型, 用来写入string长度, 数组长度, map长度, 部分数值类型
  1. @staticmethod
  2. def encode_varint(buffer: ByteBuffer, value):
  3.     '''
  4.     如果原数值是正数则将原数值变成value*2
  5.     如果原数值是负数则将原数值变成-(value + 1) * 2 + 1
  6.     相当于0->0, -1->1, 1->2,-2->3,2->4来做处理
  7.     因为小数值是常用的, 所以保证小数值及负数的小数值尽可能的占少位
  8.     '''
  9.     if type(value) == bool:
  10.         value = 1 if value else 0
  11.     real = value * 2
  12.     if value < 0:
  13.         real = -(value + 1) * 2 + 1
  14.    
  15.     for _i in range(12):
  16.         # 每个字节的最高位来表示有没有下一位, 若最高位为0, 则已完毕
  17.         b = real & 0x7F
  18.         real >>= 7
  19.         if real > 0:
  20.             buffer.write_u8(b | 0x80)
  21.         else:
  22.             buffer.write_u8(b)
  23.             break
复制代码
写入字符串, 把字符串变成索引值, 如果协议里有大量重复的字符串可大大的节约协议的长度
  1. @staticmethod
  2. def encode_str_idx(buffer: ByteBuffer, value):
  3.     '''
  4.     写入字符串索引值, 在数值区里的所有字符串默认会被写成索引值
  5.     如果重复的字符串则会返回相同的索引值(varint)
  6.     '''
  7.     idx = buffer.add_str(value)
  8.     TPPacker.encode_type(buffer, TP_DATA_TYPE.TYPE_STR_IDX)
  9.     TPPacker.encode_varint(buffer, idx)
复制代码
写入各种对应的类型

[code]@staticmethod        def encode_field(buffer: ByteBuffer, value, pattern=None):    '''    先写入类型的值(u8), 则根据类型写入类型对应的的数据    '''    if not pattern:        pattern = TPPacker.get_type_by_ref(value)    if pattern == TP_DATA_TYPE.TYPE_NIL:        return None    elif pattern == TP_DATA_TYPE.TYPE_BOOL:        TPPacker.encode_type(buffer, pattern)        TPPacker.encode_bool(buffer, value)    elif pattern >= TP_DATA_TYPE.TYPE_U8 and pattern = TP_DATA_TYPE.TYPE_U16 and pattern




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4