Schema 技术调研

2020-03-12  本文已影响0人  401

调研背景

微信支付在境外需开展各类活动,这些活动将涉及诸如活动时间、活动范围、参与者、以及各种优惠券、红包金额等数据。

此类数据通常具有以下特点:

因此将上述数据模型固化到代码中(即在程序中写死策略)或结构化到数据库中(无法应对结构的变化)都是不合适的。合适的做法是实现上述数据的配置化,为此需要开发相应的配置系统。

配置系统在不同场景下对时效、性能、数据校验等方面应当有不同的取舍,而在结合微信支付业务和技术栈进行设计与实现的过程中,对这些方面都要有所考量。

本文记录一下技术选型阶段在数据校验方面进行的调研。

诸如优惠券、红包金额等运营数据对业务影响重大,因此运营人员或开发人员对这些数据进行配置时有必要进行数据结构的校验,保障数据是合法规范的,避免非法数据破坏程序可用性,更重要的是避免因数据规则非法而导致运营事故。

确定配置数据校验方案之前需要确定配置数据本身以什么格式表达。

常见的主要有以下几种格式:

基础技术简介

  1. JSON. 即 JavaScript Object Notation(JavaScript 对象表示法)。JSON 源自于 JavaScript 但又不限于特定语言。JSON 是最常见和最基础的数据表达格式之一,其基础知识无需过多介绍。若使用 JSON 来表达配置系统的配置数据具有以下特点:

    • 拥有丰富的类库,工程实现时可得到足够支持
    • 有完善的格式校验(Schema)规范以及相应的校验工具
    • 有更高的普及度,且在满足结构化数据需求的同时足够小巧轻量,降低配置数据的门槛
  2. XML. 即 Extensible Markup Language(可扩展标记语言)。作为一种标记语言,同样可以实现数据的格式化和结构化。且能由使用者自定义标记(可扩展),因此具有足够的灵活性。同时具有完备的规则,也由于此特点导致 XML 是相对复杂和重量的表达格式,无论从人类角度还是机器角度,XML 的解读都要花费更多的功夫。若使用 XML 表达配置系统的配置数据具有以下特点:

    • 同样拥有丰富的类库,工程实现时可得到足够支持
    • 也具有相对完善的格式校验(Schema)规范以及相应的校验工具
    • 也具有较高普及度,但比较重量级,机器和人类可读性相对较低,配置数据门槛相对较高
    • 解析速度相对较慢,存储空间消耗相对较大
  3. ProtoBuf. 与上述介绍的 JSON 和 XML 不同。ProtoBuf 是一种序列化协议,偏重序列化效率和空间利用率,但由于 ProtoBuf 提供了 Message 语法,因此也可用来结构化数据。关于 ProtoBuf 的简介可参考 深入 ProtoBuf - 简介。若使用 ProtoBuf 来表达配置系统的配置数据具有以下特点:

    • ProtoBuf 本身拥有足够多的类库,对于配置数据的存储(序列化)和解析(反序列化)可得到足够支持
    • 但 ProtoBuf 并没有完善的格式校验(Schema)规范和对应校验工具。当然你也可以将 Message 本身(包含了字段、字段类型)当作其 Schema,但校验能力毕竟有限,是否可行需要结合业务场景考量。若想实现更为强大的格式校验,需要自行扩展 Message 语法和 Message 校验
    • 普及度一般,Message 可读性较差。而 ProtoBuf 数据本身并非为人类阅读设计,因此需要在 ProtoBuf 反序列化基础上提供人类可读的数据展示,最终可读性取决于使用者的实现(如可实现 ProtoBuf 转换成 Json 展示,或其它展示方案)
    • 但在解析速度、空间利用率方面具有较大优势。对比 XML,比 XML 小 3 ~ 10 倍,速度快 20 ~ 100 倍
  4. YAML. 即 Yet Another Markup Language(仍是一种标记语言),所以可以将其视作一种标记语言,但在使用中,其更加注重数据本身,因此又称为 YAML Ain't a Markup Language(YAML不是一种标记语言)。因为使用缩进表达层级关系,用空白字符和分行来分隔数据,所以其比较符合人类对层级的直观感受,人类可读性较高。YAML 可表达的数据结构有

    • 对象(键值对集合)
    • 数组(列表)
    • 纯量(单个不可再分的值,如字符串、数字等)

    可以看出 YAML 几乎可以和 JSON 的表达一一对应,只是采用缩进、分行等更直观的方式表达层级和数据分隔,在可读性上要更进一步,因此 YAML 经常用作项目内的配置文件,以下为一个 YAML 数据实例:

    languages: # 数组
      - Ruby
      - Perl
      - Python 
    websites: # 对象(键值对集合)
      YAML: yaml.org # 纯量: 字符串
      Ruby: ruby-lang.org 
      Python: python.org 
      Perl: use.perl.org 
      Count: !!str 4 # 严格类型定义(将强制类型转换)
    

    采用 YAML 来表达配置数据具有以下特点:

    • 相对上述提到的格式化方案,其类库丰富程度一般,但在工程实现时也足够使用
    • 无官方量身定制的 Schema 规范,但存在一些已有工程实现,也有直接使用 Json Schema 校验 YAML 的工程实现。整体而言,Schema 支持没有 JSON 完备
    • 普及度一般,但人类可读性较高
  5. .properties/.ini. 这里将 .properties/.ini 合在一起讲,是因为他们和上述提到的技术有本质上的区别。上述方案多为标记语言或序列化语言,具有完善与严格的规范定义。而以 .properties/.ini 为代表的则是各种各样的 “配置文件” 格式,其中 .ini 甚至没有固定的标准格式。以下是一个简单的 .ini 配置实例

    [owner]
    name=John Doe
    organization=Acme Products
    
    [database]
    server=192.0.2.42 ; use IP address in case network name resolution is not working
    port=143
    file="acme payroll.dat"
    

    .properties/.ini 代表了具有以下特点的格式化方案:

    • 只具备简单的配置数据表达方式,如 key = value, key : value 等
    • 具有简单的集合能力,如使用 [user] 表达下面配置数据属于 user 配置集合

    在开发运营配置系统之前,内部就有许多业务使用了 .ini 文件配置相关业务数据。用此类简单格式具有以下特点:

    • 一般会有对应的类库和工具,但无法与其它格式方案相比
    • 基本无相应 Schema 规范
    • 表达能力弱,通常无法表达多层级的复杂结构,对于多层级的配置人类可读性较差

格式校验简介

对 JSON、XML、ProtoBuf、YAML、.properties/.ini 做完基本介绍之后,下面进入重点即格式验证的介绍。何为格式验证?

现在以采用 JSON 表达配置系统中的配置数据为例,为了对每条配置数据进行“格式验证”,就需要对每条配置数据进行“格式定义”。

如同为了规范 Mysql、Oracle 等关系型数据库中的数据,我们会实现定义表的格式,即数据表的结构,它包含了这张表具有哪些字段,这些字段具有什么类型,这些字段又有怎样的约束(是否唯一、是否为空等),这些信息即为数据表的 Schema。Schema 将为我们校验和保障数据表中的数据是符合一定规范的。

为了对配置系统的配置数据进行类似的校验,保证其中数据符合规范,就需要有相应的 Schema 技术。以下介绍不同格式下的 Schema 验证技术。

XML 数据格式验证

XML DTD
DTD 即 Document Type Definition(文档类型定义),顾名思义是一种定义 XML 文档结构的技术。以下为一个实例:

<!DOCTYPE NEWSPAPER[

<!--  -->
<!--
  CDATA(character data)表示字符数据,不会被解析器解析,可视为纯文本。
  
  PCDATA(parsed character data)表示被解析的字符数据
    即该数据会被 XML 解析器解析,固其中不能直接包含 &、< 或 > 字符
    而应该使用 &amp;、&lt; 以及 &gt; 来代替。 -->

<!ELEMENT NEWSPAPER (ARTICLE+)>
<!ELEMENT ARTICLE (HEADLINE,BYLINE,LEAD,BODY,NOTES)>
<!ELEMENT HEADLINE (#PCDATA)>
<!ELEMENT BYLINE (#PCDATA)>
<!ELEMENT LEAD (#PCDATA)>
<!ELEMENT BODY (#PCDATA)>
<!ELEMENT NOTES (#PCDATA)>

<!ATTLIST ARTICLE AUTHOR CDATA #REQUIRED>
<!ATTLIST ARTICLE EDITOR CDATA #IMPLIED>
<!ATTLIST ARTICLE DATE CDATA #IMPLIED>
<!ATTLIST ARTICLE EDITION CDATA #IMPLIED>

<!ENTITY NEWSPAPER "Vervet Logic Times">
<!ENTITY PUBLISHER "Vervet Logic Press">
<!ENTITY COPYRIGHT "Copyright 1998 Vervet Logic Press">

]>

DTD 主要包含了以下几个模块:

上述 DTD 规定了 XML 文档的根元素是 NEWSPAPERNEWSPAPER 下为 ARTICLE 元素,且 ARTICLE 元素至少出现一次。ARTICLE下面又有 HEADLINE、BYLINE、LEAD、BODY、NOTES 五个元素。同时 ARTICLE 元素又具有 AUTHOR、EDITOR、DATE、EDITION 四个属性......

通过 DTD 可以 XML 数据进行格式的定义,即 DTD 可视为 XML 数据的 Schema,再配合相应的校验工具就可以校验 XML 是否符合 DTD 中定义的格式。

但由于 DTD 自身较为复杂,因此解析起来有诸多不便,因此现在 DTD 应用越来越少。

XML Schema(XML Schema Definition)
W3C 于 2001 年推荐了一种基于 XML 的 DTD 代替者,为 XML Schema Definition,可称为 XSD。

这里需要注意 XML Schema 与 XML Schema Definition 的区别。

从广义上来讲,XML Schema 指对 XML 进行 Schema 描述的语言,这里完全可以有不同语言或规范来定义 XML Schema 语言,事实上确实存在各种各样的语言能够实现对 XML 的 Schema 描述,这些语言都可以称为 XML Schema[9]

但 W3C 官方推出了针对 XML 进行 Schema 校验的语言,自然要来 “占有” XML Schema 这个名称,所以 XML Schema 狭义上可特指 W3C 推出的标准 Schema 语言。但为了区分,我们通常可将官方的标准语言称为 XSD。

XSD 采用 XML 编写,可复用 XML 解析器。这无疑降低了学习成本,相对 DTD 的复杂,XSD 可以说更为易读易懂。看一个简单实例:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.w3schools.com"
xmlns="http://www.w3schools.com"
elementFormDefault="qualified">

<xs:element name="note">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="to" type="xs:string"/>
      <xs:element name="from" type="xs:string"/>
      <xs:element name="heading" type="xs:string"/>
      <xs:element name="body" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

</xs:schema>

上述实例表达了 note 是一个 complexType 复合类型,它包含其它子元素,分别为 to、from、heading、body,且这些子元素均为简单类型(字符串)。此外 XSD 还支持定义取值范围限定、元素的顺序、组合等定义。

XSD 虽是官方支持和推荐的 XML Schema 标准语言,但调研后发现其工程实现较少。这与 XML 本身在网络传输方面被 JSON、ProtoBuf 取代有一定的关联。除了在学校里接触过之外,在本人之前的实际工程中确实还未使用过 XSD。因此此方案选择度较低。

DTD 与 XML Schema 都是曾经应用广泛的技术,但如今已逐渐被其它技术取代,我们对其有所了解即可。感兴趣可以参考本文末尾的 [参考资料] 翻阅更多详细介绍。

JSON 数据格式验证

Json Schema

JSON Schema From Wikipedia
JSON Schema specifies a JSON-based format to define the structure of JSON data for validation, documentation, and interaction control. It provides a contract for the JSON data required by a given application, and how that data can be modified.

JSON Schema 基于 JSON 的语法格式, 定义了 JSON 数据的结构,以进行 JSON 数据的验证、文档编制和交互控制。它为应用程序所需的 JSON 数据以及如何修改 JSON 数据提供了规范和合约。

看一个 Json 实例:

{
  "name": "eren",
  "age":23,
  "address": "SZ",
  "phone": "132xxx",
  "email": "xxx@foxmail.com",
}

可定义 Json Schema 如下:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "Person",    // 标题
  "description": "a person", // 关于 Schema 的描述
  "type": "object",     // 指明根字节是一个对象类型
  "properties": {       // 键值对定义
    "name": {           // 指明含有 name 字段
      "type": "string", // 指明字段类型
      "minLength": 2,   // 设定字段最小、最大长度 
      "maxLength": 20,
    },
    "age": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100
    },
    "address": {
      "type": "string",
      "enum": ["BJ", "SH", "SZ", "HZ"] // 限定取值枚举
    },
    "phone": {
      "type": "string"
    },
    "email": {
      "type": "string"
    }
  },
  "required": ["name", "age"] // 指明必填字段
}

除上述涉及到的定义外,还有 pattern、uniqueItems、minProperties、maxProperties 等等属性。详情可参阅 官方文档

相比于上述所提到的 XML 相关的 Schema 技术, JSON Schema 在目前具有更普及的应用,同时也能提供更为强大的类库支持。

官方网址 列出了丰富的不同语言的校验类库。运营配置系统中就使用到了 JavaScript 的 ajv 以及 C++ 的 rapidjson

ProtoBuf、YAML、.properties/.ini 的 Schema 的现状已在本文的 基础技术简介 一节中介绍,不再赘述。

以上。

参考资料

上一篇下一篇

猜你喜欢

热点阅读