【问题标题】:How to ensure thrift objects are backward compatible?如何确保 Thrift 对象向后兼容?
【发布时间】:2016-12-29 12:26:21
【问题描述】:

我们目前正在使用 thrift 来开发我们的微服务。 当我最近遇到以下问题时。

假设下面是摘要对象的节俭合约,并且有一个 API 使用传递的摘要对象获取和更新摘要。

版本 - 1.0

struct Summary {
    1: required string summaryId,
    2: required i32 summaryCost
}

Summary getSummary(1: string summaryId);

void updateSummary(1: Summary summary);

现在假设有 5 个服务正在使用这个 1.0 总结合同。
在下一个版本中,我们添加了另一个对象,称为 summaryvalues 列表

所以新合同看起来像

版本 - 2.0

struct Summary {
    1: required string summaryId,
    2: required i32 summaryCost,
    3: optional list<i32> summaryValues
}

Summary getSummary(1: string summaryId);

void updateSummary(1: Summary summary);
  1. 因此,当填充下面的列表时,我们会保存 summaryValues aganist 的值列表 summaryId
  2. 当客户端将此列表发送为null 时,我们会删除为该“summaryId”保存的现有值。

现在,当使用 OLDER 版本的 thrift 合约(Version 1.0)的其他服务尝试调用 getSummary 和 updateSummary 时,会出现问题 .
旧客户端调用 updateSummary 的目的是为 summaryCost 设置另一个值。但是,由于此客户端不包含对象 summaryValues,它会将带有 summaryValues 为 null 的 Summary 对象发送到服务器。

这会导致服务器删除该 summaryValues 的所有现有值 summaryId

有没有办法在节俭中处理这个问题? isSet() 方法在这里不起作用,因为它们尝试执行简单的空值检查。
每次我们发布对现有对象进行修改的新客户端时,我们都必须强制升级其他服务器的客户端版本,即使更改与它们无关。

【问题讨论】:

  • FWIW 这里的核心问题:“如果存储对象 {a: 42, b: 99, c: 7} 的服务从不知道 c 字段的客户端接收到将其更新为 {a: 0, b: 0} 的调用,那么应该分配c 的值吗?”不是 Thrift 特有的。

标签: java thrift microservices


【解决方案1】:

在您的version 2.0 中,updateSummary() 方法的合同已更改(即,现在它允许保存和删除汇总值):

选项 1: 与其改变其行为,不如创建一个新方法(即updateSummaryV2())并开始在最新版本的客户端中使用它,同时弃用旧版本。

这样,旧版本的客户端仍然使用正常的updateSummary(),而不会与新方法的约定和假设发生冲突。


选项 2: 添加一个包含 api 版本的可选字段,并将默认值设置为最新的 API 版本:

struct Summary {
    1: required string summaryId,
    2: required i32 summaryCost,
    3: optional list<i32> summaryValues
    4: optional i32 apiVersion = 2
}

这样,如果未设置apiVersion,您就知道请求来自旧客户端,对于未来的版本,您将知道客户端版本并做出相应的反应。


或者,您只能在提供空列表时删除记录,如果未设置列表,则不执行任何操作,以遵守之前的方法约定。

附带说明:即使不考虑交叉兼容性问题,让动作依赖于隐含的东西(这里是没有列表)通常也是有风险的。当此类操作依赖于显式标志时,它通常更安全(并且更易于使用和维护)。

【讨论】:

  • 创建另一个 API 会使代码的维护变得非常麻烦,因为我们需要记住作为哪个 API 的一部分添加了哪些功能。事情很容易失控。此外,可能不可能有一个空列表,因为我们必须通知所有依赖客户端添加此行为,即使他们对此字段不感兴趣。
  • “我们需要记住作为哪个 API 的一部分添加了哪些功能”-> 这听起来像是在多版本 API 上下文中无论如何你都必须要做的事情:)。我已经用另一个选项更新了答案。
  • @BandiKishore -> 对第二个选项有任何反馈吗? :)
  • +1 以获得不同的建议,但正在寻找更清洁的解决方案 :) 这实际上是我们正在考虑使用的解决方案之一,而不是依赖开发人员每次都更新此版本的价值一个特性是,我们考虑使用客户端 jar 本身的版本(在使用${project.version} 构建期间填充在属性文件中)并使用它来识别客户端正在使用的版本。 由于限制将评论一分为二
  • 我们没有继续采用该解决方案,因为我们认为它不干净而且看起来更像是一种黑客行为。此外,我们认为更好的方法是使用单独的标志(针对每个功能),当客户想要清除该值时会设置这些标志。对于较小的特征,这将是首选,但对于修改了嵌套对象的较大特征,我们仍然没有更简洁的解决方案。
猜你喜欢
  • 1970-01-01
  • 2011-02-09
  • 1970-01-01
  • 1970-01-01
  • 2011-07-08
  • 2012-02-13
  • 1970-01-01
  • 1970-01-01
  • 2013-08-11
相关资源
最近更新 更多