如何最佳实践Thrift文件(API)版本控制?

5

我有一个使用Thrift编写的API。例如:

service Api {
  void invoke()
}

它有一个功能。我想改变它的行为,让它做另外一些事情,但仍然保留旧版本的行为,以满足期望旧版本行为的客户。

处理新API版本的最佳实践是什么?

2个回答

5

软件版本控制

Thrift支持软件版本控制,因此可以完全合法地进行服务的第二个版本,其代码如下:

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,你将需要一直保留这个成员,直到整个结构体被移除。同样的道理也适用于后续添加新的required字段。否则,你会面临破坏性变化的风险,因为混合使用旧客户端和新客户端或服务器最终会产生这样的情况:一端绝对期望某个required成员字段,但是另一端无法提供,仅仅是因为它对此一无所知。

这不是普通或optional字段的问题,因为Thrift设计为跳过未知字段(类型ID包含在线路数据中),并忽略缺失的字段。相比之下,required字段应用额外的检查以确保它们存在于线路数据中。

端点

虽然软版本控制是一个很好的工具,但它的代价是由于需要兼容而累积负担。此外,在某些情况下,你的API将会经历破坏性变化,有意地而不是向后兼容。在这种情况下,建议将新服务设置为不同的端点。

或者,可以使用Thrift 0.9.2引入的multiplex协议在同一端点(即套接字、http URI等)上提供多个服务和/或服务版本。


0
对于您的特定情况,您可以添加一个新方法(命名为其他名称),以执行新功能。将来,我建议避免使用invoke或类似的方法名称,正是因为这个原因。现在,您的服务将有一个invoke方法,它执行某些未知操作,还有另一个方法(希望命名更好),它执行其所说的操作。这可能会导致用户混淆,但一切仍将正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接