【发布时间】:2015-09-01 08:00:50
【问题描述】:
我有一个用 Thrift 编写的 API。示例:
service Api {
void invoke()
}
它做了一些事情。我想更改行为以执行其他操作,但仍为期望旧行为的客户保留旧行为。
处理新 API 版本的最佳做法是什么?
【问题讨论】:
标签: versioning thrift
我有一个用 Thrift 编写的 API。示例:
service Api {
void invoke()
}
它做了一些事情。我想更改行为以执行其他操作,但仍为期望旧行为的客户保留旧行为。
处理新 API 版本的最佳做法是什么?
【问题讨论】:
标签: versioning thrift
Thrift 支持软版本控制,因此执行您的服务的版本 2 是完全有效的,如下所示:
service Api {
void invoke(1: string optional_arg1, 2: i32 optional_arg2) throws (1: MyError e)
i32 number_of_invokes()
}
由于新添加的参数在技术上是可选的,任意客户端请求可能包含也可能不包含它们,或者可能只包含它们的一部分(例如,指定 arg1 但不指定 arg2)。异常有点不同,老客户端会引发某种通用的意外异常或类似异常。
甚至可以完全删除一个过时的函数,在这种情况下,当旧客户端尝试调用(现在不存在的)已删除函数时,他们会收到异常。
关于将成员字段添加到结构、异常等方面,上述所有内容同样适用。建议不要从 IDL 文件中删除声明,而是注释掉旧删除的成员字段和防止人们在以后的版本中重复使用旧的字段 ID、旧的函数名或旧的枚举值的功能。
struct foobar {
// API 1.0 fields
1: i32 foo
//2: i32 bar - obsolete with API 2.0
// API 2.0 fields
3: i32 baz
}
required 是永远的您需要注意的是关键字required 的使用。一旦您发布了一个包含required 成员的结构的 API,您将需要携带这个 API,直到整个结构被删除。稍后添加新的required 字段也是如此。否则,您可能会面临破坏更改的风险,因为混合新旧客户端和服务器迟早会产生一种情况,即一端绝对期望某个required 成员字段,但另一端无法交付,只是因为它什么都不知道关于它。
这不是普通或optional 字段的问题,因为 Thrift 旨在跳过未知字段(类型 ID 包含在连线数据中),而只是忽略丢失的字段。相反,对required 字段应用额外检查,以确保它们存在于有线数据中。
虽然软版本控制是一个很好的工具,但由于需要兼容,它以累积负担为代价。此外,在某些情况下,您的 API 将进行重大更改,故意不向后兼容。在这种情况下,建议将新服务设置在不同的端点。
另外,Thrift 0.9.2 引入的multiplex protocol 可用于在同一端点(即套接字、http URI 等)上提供多个服务和/或服务版本
【讨论】:
对于您的特定场景,您可以添加一个新方法(命名为其他)来执行新操作。将来,出于这个确切原因,我将避免命名方法 invoke 或类似方法。您的服务现在将有一个方法 invoke 来做一些未知的事情和另一个方法(希望命名更好),它会按照它所说的去做。这可能会导致您的用户感到困惑,但一切仍然有效。
【讨论】: