本章的标题表达了作者的主旨:Good Protocols Make Good Practice。的确,好的协议或文件格式,会大大提升使用者的效率。这里的文本化,包括两类,一是通信协议,二是数据文件(包括配置文件,日志文件等)。下面分别讨论这两种数据的常用格式。
1. 数据文件
UNIX对于文件处理通常基于流和行处理,因此,数据文件多采用文本化协议,方便阅读与编辑,以及扩展。
配置文件常用的格式有ini,xml格式,比较推荐的是xml格式的,xml分层的结构和灵活的设计可以很方便的扩展并且保存向下兼容。另外一种系统类配置文件如系统的密钥等,常使用分隔符进行区分,这一点是因为这类文件通常是行存储后,不需要太强的扩展性,清晰与精简是第一位的,此外,也方便使用sed、awk等工具。
日志文件一般分为两种,一种是出错日志,另一种是流水日志。出错日志旨在方便使用者快速定位问题,因此格式不需要太拘泥,只要把错误码、错误信息、代码行和其它特殊信息如登陆ID等打印清楚即可;流水日志一方面是数据记录,更重要功能是进行数据分析和数据提取,而可读性是次要的,基于这个原则流水日志的格式推荐DSV风格,使用如竖线、下划线等进行分隔,方便使用awk进行提取。
2. 通信协议
文中对文本化的通信协议十分推崇,的确,对于应用层的协议而言,易用性和可读性十分重要,以便于使用者进行分析。大家可以想象,如果HTTP协议是二进制的话,大家的开发效率会大大折扣。而对于具体业务而言,特别是一些高性能和高并发的server而言,二进制格式的高效和对带宽的节省优势就十分明显了。因此,具体采用什么格式,还是对通用性、可读性、性能、扩展性的平衡。
下面,总结下我在项目中常用的协议和文本格式。
1. 协议
现在的项目分为三层:接入层、逻辑层和数据层,请求流向是“前台-WEBServer-LogicServer-DB”。协议主要采用json格式和二进制格式,前台和cgi之前是http协议,数据采用json格式,方便前台js解析。 cgi和logicserver之前使用tcp或udp,数据采用二进制格式,但由于方便扩展,二进制格式中头部格式固定,信息体部分用json格式,方便扩展。格式如下所示:
typedef struct base_header
{
uint32_t version; /* 版本号*/
uint32_t magic; /* 协议魔数 */
uint32_t length; /* 协议长度 */
uint32_t serial; /* 协议包编号 */
uint32_t command; /* 操作命令字 */
base_header()
{
version = 0;
magic = 0;
length = 0;
serial = 0;
command = 0;
}
}__attribute__((packed)) base_header_t;
struct request_protocol_t
{
base_header_t base_header; /* 协议头 */
uint32_t id; /* 用户ID*/
uint32_t domain; /* 域名*/
uint32_t param_length; /* 请求参数长度 */
char json_param[0]; /* 请求json数据 */
request_protocol_t()
{
id = 0;
domain = 0;
param_length = 0;
}
}__attribute__((packed));
2. 配置文件
配置文件分两种,一种是以文件格式保存的,另一种是动态调整的通常保存在cache中。文件格式保存的,通常使用xml的格式,方便阅读与解析;保存在cache中的配置,可以使用xml或者json,xml的可读性较好,而json的解析更加方便,有第三方库直接把json数据解析到一个json::value的结构中,但可读性略差了一点。
3. 日志
日志格式很重要,在制定日志格式时,需要考虑使用者的便利性。出错日志不必多说,下面主要说下流水日志。项目中常用的流水有三种流水:支付流水;回滚失败流水;回档或补偿流水。这三种流水格式要求基本一致,对可读性要求不高,主要是方便数据提取和统计。如支付流水常用于对账,回滚失败流水常用在数据一致性校对,回档和补偿流水多用于数据备份。
以补偿流水为例,假设由于某些原因需要给用户的仓库补偿若干物品,那么需要记录的数据如下:
用户ID,仓库中所有物品对应的补偿前的数量,补偿后的数量。
这里记录流水的原因有两个:一是如果补偿出错时,可以根据补偿前的数量进行数据恢复,二是补偿完后用于和用户进行对帐应付投诉。可以采用下面的格式:
userid item1_num1|item2_num2 item1_num1|item2_num2|item3_num3
用户ID 补偿前仓库数据(物品1ID_物品1数量|物品2ID_物品2数量) 补偿后仓库数据(格式同前)
这里使用的分隔包括空格,竖线和下划线,使用这种格式,可以很方便的用awk提取或者写工具逐行处理。
总之,在设计协议或数据文件时,采用文本格式还是二进制格式,以及如何制定格式,最终要站在使用者的立场上进行考虑,易用性、扩展性、性能,需要综合考虑,权衡利弊,多设计一些场景,从而可以挖掘更多的需求,也使得格式更方便地满足更多需求。