diff --git a/ch4.md b/ch4.md index fe82cda5..81fad1ed 100644 --- a/ch4.md +++ b/ch4.md @@ -11,7 +11,7 @@ [TOC] -应用程序不可避免地随时间而变化。新产品的推出,对需求的深入理解,或者商业环境的变化,总会伴随着**功能(feature)** 的增增改改。[第一章](ch1.md)介绍了**可演化性(evolvability)**的概念:应该尽力构建能灵活适应变化的系统(请参阅“[可演化性:拥抱变化](ch1.md#可演化性:拥抱变化)”)。 +应用程序不可避免地随时间而变化。新产品的推出,对需求的深入理解,或者商业环境的变化,总会伴随着**功能(feature)** 的增增改改。[第一章](ch1.md)介绍了**可演化性(evolvability)** 的概念:应该尽力构建能灵活适应变化的系统(请参阅“[可演化性:拥抱变化](ch1.md#可演化性:拥抱变化)”)。 在大多数情况下,修改应用程序的功能也意味着需要更改其存储的数据:可能需要使用新的字段或记录类型,或者以新方式展示现有数据。 @@ -90,7 +90,7 @@ JSON,XML和CSV属于文本格式,因此具有人类可读性(尽管它们 JSON比XML简洁,但与二进制格式相比还是太占空间。这一事实导致大量二进制编码版本JSON(MessagePack,BSON,BJSON,UBJSON,BISON和Smile等) 和 XML(例如WBXML和Fast Infoset)的出现。这些格式已经在各种各样的领域中采用,但是没有一个能像文本版JSON和XML那样被广泛采用。 -这些格式中的一些扩展了一组数据类型(例如,区分整数和浮点数,或者增加对二进制字符串的支持),另一方面,它们没有改变JSON / XML的数据模型。特别是由于它们没有规定模式,所以它们需要在编码数据中包含所有的对象字段名称。也就是说,在[例4-1]()中的JSON文档的二进制编码中,需要在某处包含字符串`userName`,`favoriteNumber`和`interest`。 +这些格式中的一些扩展了一组数据类型(例如,区分整数和浮点数,或者增加对二进制字符串的支持),另一方面,它们没有改变JSON / XML的数据模型。特别是由于它们没有规定模式,所以它们需要在编码数据中包含所有的对象字段名称。也就是说,在[例4-1]()中的JSON文档的二进制编码中,需要在某处包含字符串`userName`,`favoriteNumber`和`interests`。 **例4-1 本章中用于展示二进制编码的示例记录** @@ -152,7 +152,7 @@ Thrift和Protocol Buffers每一个都带有一个代码生成工具,它采用 与[图4-1](Img/fig4-1.png)类似,每个字段都有一个类型注释(用于指示它是一个字符串,整数,列表等),还可以根据需要指定长度(字符串的长度,列表中的项目数) 。出现在数据中的字符串`(“Martin”, “daydreaming”, “hacking”)`也被编码为ASCII(或者说,UTF-8),与之前类似。 -与[图4-1](img/fig4-1.png)相比,最大的区别是没有字段名`(userName, favoriteNumber, interest)`。相反,编码数据包含字段标签,它们是数字`(1, 2和3)`。这些是模式定义中出现的数字。字段标记就像字段的别名 - 它们是说我们正在谈论的字段的一种紧凑的方式,而不必拼出字段名称。 +与[图4-1](img/fig4-1.png)相比,最大的区别是没有字段名`(userName, favoriteNumber, interests)`。相反,编码数据包含字段标签,它们是数字`(1, 2和3)`。这些是模式定义中出现的数字。字段标记就像字段的别名 - 它们是说我们正在谈论的字段的一种紧凑的方式,而不必拼出字段名称。 Thrift CompactProtocol编码在语义上等同于BinaryProtocol,但是如[图4-3](img/fig4-3.png)所示,它只将相同的信息打包成只有34个字节。它通过将字段类型和标签号打包到单个字节中,并使用可变长度整数来实现。数字1337不是使用全部八个字节,而是用两个字节编码,每个字节的最高位用来指示是否还有更多的字节来。这意味着-64到63之间的数字被编码为一个字节,-8192和8191之间的数字以两个字节编码,等等。较大的数字使用更多的字节。 @@ -248,9 +248,9 @@ Avro的关键思想是Writer模式和Reader模式不必是相同的 - 他们只 使用Avro,向前兼容性意味着您可以将新版本的模式作为Writer,并将旧版本的模式作为Reader。相反,向后兼容意味着你可以有一个作为Reader的新版本模式和作为Writer的旧版本模式。 -为了保持兼容性,您只能添加或删除具有默认值的字段。 (我们的Avro模式中的字段`favourNumber`的默认值为`null`)。例如,假设您添加了一个有默认值的字段,这个新的字段将存在于新模式而不是旧模式中。当使用新模式的Reader读取使用旧模式写入的记录时,将为缺少的字段填充默认值。 +为了保持兼容性,您只能添加或删除具有默认值的字段。 (我们的Avro模式中的字段`favoriteNumber`的默认值为`null`)。例如,假设您添加了一个有默认值的字段,这个新的字段将存在于新模式而不是旧模式中。当使用新模式的Reader读取使用旧模式写入的记录时,将为缺少的字段填充默认值。 -如果你要添加一个没有默认值的字段,新的Reader将无法读取旧Writer写的数据,所以你会破坏向后兼容性。如果您要删除没有默认值的字段,旧的Reader将无法读取新Writer写入的数据,因此您会打破向前兼容性。在一些编程语言中,null是任何变量可以接受的默认值,但在Avro中并不是这样:如果要允许一个字段为`null`,则必须使用联合类型。例如,`union {null,long,string} field;`表示field可以是数字或字符串,也可以是`null`。如果要将null作为默认值,则它必须是union的分支之一[^iv]。这样的写法比默认情况下就允许任何变量是`null`显得更加冗长,但是通过明确什么可以和什么不可以是`null`,有助于防止出错【22】。 +如果你要添加一个没有默认值的字段,新的Reader将无法读取旧Writer写的数据,所以你会破坏向后兼容性。如果您要删除没有默认值的字段,旧的Reader将无法读取新Writer写入的数据,因此您会打破向前兼容性。在一些编程语言中,null是任何变量可以接受的默认值,但在Avro中并不是这样:如果要允许一个字段为`null`,则必须使用联合类型。例如,`union {null, long, string} field;`表示field可以是数字或字符串,也可以是`null`。如果要将null作为默认值,则它必须是union的分支之一[^iv]。这样的写法比默认情况下就允许任何变量是`null`显得更加冗长,但是通过明确什么可以和什么不可以是`null`,有助于防止出错【22】。 [^iv]: 确切地说,默认值必须是联合的第一个分支的类型,尽管这是Avro的特定限制,而不是联合类型的一般特征。 @@ -473,7 +473,7 @@ RPC方案的前后向兼容性属性从它使用的编码方式中继承: #### 消息代理 -过去,**消息代理(Message Broker)**主要是TIBCO,IBM WebSphere和webMethods等公司的商业软件的秀场。最近像RabbitMQ,ActiveMQ,HornetQ,NATS和Apache Kafka这样的开源实现已经流行起来。我们将在[第十一章](ch11.md)中对它们进行更详细的比较。 +过去,**消息代理(Message Broker)** 主要是TIBCO,IBM WebSphere和webMethods等公司的商业软件的秀场。最近像RabbitMQ,ActiveMQ,HornetQ,NATS和Apache Kafka这样的开源实现已经流行起来。我们将在[第十一章](ch11.md)中对它们进行更详细的比较。 详细的交付语义因实现和配置而异,但通常情况下,消息代理的使用方式如下:一个进程将消息发送到指定的队列或主题,代理确保将消息传递给那个队列或主题的一个或多个消费者或订阅者。在同一主题上可以有许多生产者和许多消费者。