开放银行虽然有多种对外连接方式,但API以其易用性成为了接受度最好的选择,也是很多综合性银行推广开放银行必然需要对外提供的核心能力。
简单的API之下,蕴藏着复杂的API支撑体系,不管是设计时的命名和路由规范,还是运行时的安全和容错处理,深入细节都让人生畏。
欣慰的是,随着技术的持续演进,越来越多的经验被逐步提炼成了标准和工具,让整个API的治理持续简化。特别是在英国和新加坡政府主动推进开放银行(Open Banking)之后,金融服务领域在API方面有了越来越多的资产可以为各家银行所采用。
如果想体验一下开放API为金融行业发展带来的便利,可以参考《开发者体验 – 开放银行的奠基石》文中对行业领先者 —— 德意志银行的分析。
(2021年,英国开放银行组织庆祝成立3周年,https://www.openbanking.org.u...
然而API版本(Versioning)设计一直是开放平台设计上的老大难问题。
就像过去几十年支持了我们几大行高速发展的主机系统,想要迁移到开放平台,都是一次庞大的再造工程。而开放银行本身的商业模式,又决定了API一旦发布,围绕其生态的干系方是完全不由银行控制的。很多银行人都经历过系统报文格式的切换,切换过程中细枝末节的问题经常造成大家挑灯夜战。但报文本身毕竟还是同业圈子内的升级,可控性较好,开放银行却是涉及到多元化异业的分布式协作,没有任何一方有绝对的控制力。
API版本策略也很难要求各大银行都完全一致,必然受制于各家开放业务想法的不同,和内部系统架构的架构差异。所以具体采用什么策略实际上是一个业务和技术的综合决策,而业务和技术在API版本策略上的对齐往往被大家忽略,结果就是业务认为加个接口字段很简单,开发却抱怨破坏了版本一致性,造成巨大的外部兼容性问题。
下面就让我们从一个相对综合的视角来研讨一下开放API的版本模式,促进银行业在开放API平台建设方面更多碰撞和思考。
软件版本 VS API版本
现代社会每个人都是软件用户,特别是在智能手机普及的今天,各种各样的软件已经融入到了我们日常生活中。对于软件的“升级”是一件习以为常的事情,很多时候我们都会被提示软件版本过低,需要升级才能使用某功能。
移动APP时代对于软件版本管理实质上是做了大量的简化,不管是苹果的iOS平台,还是安卓Android平台,APP软件的版本都是一直向前升级的。如果突然打开一款久不使用的APP,提示版本太低不能使用,或者由于手机操作系统太老旧而无法使用新版本,我们都会觉得是常理之中的事情,不会有啥针对软件开发者本身的抱怨。
甚至如果一款APP超过两周都没有任何改变,我们会怀疑其活跃程度,是不是背后老板已经跑路了。APP软件版本升级成了和服务一起演进的事情,服务越来越好、功能越来越多,自然要求发布新的软件版本,比如双十一前,各大电商肯定是要更新几次自家APP的。
当然智能手机之外我们其实还有很多的大型商业软件,比如每家企业现在都需要的财务管理软件,这些软件在市面上存在着多个版本,由于行业、规模、经营各家企业都不尽相同,所以这样的商业软件用不同的版本适配不同的企业是很常见的。
这之上也有企业几年前买了一个软件版本,由于各种原因,一直使用没有升级,也或许确实满足了企业的需求,没有必要采纳新版本。版本本身在这样的商业软件交易时就是其中一个关键属性,并且一般都有针对后续发布新版本是否采纳方面的合同条款。
开放API版本兼具了以上两种软件的特点,但又提出了更高的要求。由于商业合作和生态的持续丰富,开放API显然会持续升级,而且新的版本应该比旧的版本更加高效(不排除功能上有增加,比如访问鉴权),常见的需求是过去1分钟100笔交易,现在要求1万笔。作为API的使用者,期待和更新手机APP一样,新版本应该更强大。
不同于手机APP的是,开放API升级后不可能强行要求“用户”升级,外部使用API的系统更新或改变都不是API发布者能掌控的,并且也不可能通过类似iOS这样的平台去统一使用要求,比如安全合规方面的统一升级。
开放API同时也是被多方使用的,和商业软件一样,最开始连接时采用的是当时的API版本,后续有的连接希望一直用下去,有的可能提出新的述求。当新的述求被新的API版本满足后,作为之前的API使用者,我们会默认新版本不会破坏之前的服务,即使新功能对于部分使用者来说没有采用。
商业软件的世界里,我们经常会听到版本5.0是一个大升级,必须重新安装,不支持从4.0升级。这时我们一般有些小懊恼,但卸载4.0,安装5.0也不是太大的困难。然而如果某API的5.0版本不兼容4.0版本,也就意味着4.0的使用者们需要重新修改自己的系统,当我们有成百上千的外部应用使用该API时,这就是一个巨大的挑战,很多时候甚至不得不面临用户的投诉。
API版本致胜原则
迎接这样的挑战,我们首先需要明确开放API版本升级的是什么?
首先API是用来传递信息的,信息显然必须遵循一定的格式。银行从业者立马就能想到报文,比如全球通行的SWIFT报,每种报文必然需要有相关的字段设计和格式要求,信息的提供者和消费者都必须根据格式规范来,要不然就没法交流了。当然这样的格式调整最好少做,每次都带来了巨大的工作量,收益却有限。
(SWIFT报文示例,报文格式本身是信息传递的一部分。任何格式的改变都要求收发方必须同步,这对于面向开放的互联网来说是不现实的)
报文的使用源自于电报时代的点对点信息沟通,却不是我们这个互联网时代所说的API。API提供的信息某种意义上是针对业务实体的操作,比如增删查改(CRUD),API的命名其实就是要描述这样的操作。这里我们不纠结于API采用的实现架构(RESTful和RPC),专注于API版本背后的实质,就会发现其实需要被管理的是业务实体的变化。
类比一下汽车制造商常用的版本控制方式,奔驰宝马这样的汽车制造商通常每4、5年推出新一代的流行车型,比如宝马的X系列。接下来每年都会有升级版本,比如2019年的X1和2018年的X1性能不同,而且外形上也有稍许不同。每个客户购买的汽车是特定型号的特定版本汽车,如果制造商召回汽车来修复故障,也需要明确型号和版本,当然后续维修是无法使您2018版本汽车的外观及行为和2019年版本一样的。
回到金融领域,我们的业务实体很多,比如某个客户的某种账户、某种类型交易、债券衍生品等等。开放API提供了让我们的合作伙伴能够操作这些实体,比如客户想通过第三方支付应用调转自己账户里的存款。显然我们不会把客户账户的信息以报文的形式传递给第三方,API提供的是第三方在客户提供授权信息的情况下,能够告知我们将客户的资金转往哪个指定账户。
所以API版本的背后是我们业务实体的变化,比如由于监管合规的要求,客户账户需要进行更进一步的实名验证,更多的信息需要采集和存储,这就扩展了客户账户这个实体的设计。
很显然这样的业务实体变化,在接下来金融服务开放普惠的主旋律下是不可避免的,这也是开放API平台版本策略为什么是必须考虑的一个核心治理问题,好的开放API版本策略必然需要满足以下两大原则。
原则一:平台独立性
无论内部如何实现API,任何客户端都应该能够按照发布的规则调用API。这就要求API使用开放的标准协议,并具有一种机制,让客户端和Web服务端可以就要交换的数据格式达成一致。在整个连接过程中,API提供方不关注客户端使用方来自于什么平台,即不因为侦测到iOS系统发来的请求,就提供不同于Android系统的处理和反馈。
互联网几十年的高速拓展实际上给我们提供了一整套基于HTTP协议的运作机制,而这套机制是我们推荐开放API设计者们去拥抱的。如果将浏览器比作基于HTTP协议的Web操作系统,那任何的浏览器都需要实现相关的协议标准,从而能够进行Web信息的请求和呈现。
原则二:服务兼容性
开放API应该能够独立于客户端应用程序进行演化和添加功能。随着API的发展,现有的客户端应用程序应继续运行而无需修改。而且所有功能,比如新的安全认证方式,都应该是可被发现的,以便客户端应用程序可以充分使用它。
基于HTTP的RESTful架构也给我们提供了很多处理兼容性的手段,超媒体(Hypermedia)的提出某种意义上就是为了解决网络上开放接口请求兼容性问题而设计的。当然超媒体的引入会带来整个接口设计、开发到运维成本的较大增长,所以在API设计上,针对不同的上下文(复杂度)也会有很多的处理方式。
一言以蔽之,开放API版本的致胜之道就是让使用方感觉“没有版本”,客户端从设计、开发,到发布、运营都不会因为某个API的升级而出现服务中断。
主流API版本模式
我们谈开放API的基础环境是互联网,所以HTTP毫无疑问是必须采用的协议。如前文提到,在版本策略设计上,希望尽量不要耦合于特定的实现模式,虽然RESTful是我们推荐的实现方式,但也理解在一些场景下设计者会倾向于描述更为直接的WebSocket模式。
(有能力的银行也可以效仿亚马逊AWS提供两套实现架构,便于使用者根据自身场景灵活选用。https://aws.amazon.com/cn/api...
基于开放API的致胜原则,在处理版本升级挑战上,我们认为有以下四种常见模式供大家参考。每种模式并非是完全独立的,很多时候我们看到不少开放API平台也动态采用着多种模式。
模式一:头文件定制版本字段
由于HTTP协议头文件里允许定制字段的扩展,我们可以采用 “X-MyAPIVersionRequest-Header: 2.0” 这样的方式来表达请求的myapi是版本2.0。
假设针对客户账户支付的一个开放API,1.0版本主要是针对同业的银行合作伙伴,希望在2.0支持持牌的金融科技公司。针对金融科技公司我们需要做额外的风控和限额,那么就可以要求金融科技企业的客户端在API请求中必须包含这样一个扩展的版本头文件字段。未来也可以针对更多的客群,比如小微商户,发布3.0、4.0,通过版本来明确针对同一业务实体,同一操作的不同步骤。
由于是定制字段,使用方在设计自身的客户端调用时,是需要理解游戏规则的。对于API提供方来说做到了较好的兼容性,但扩展性却受到了一定的制约,毕竟定制化知识的传播成本是远高于通用型标准的。
模式二:媒体类型扩展版本信息
同样是利用HTTP协议头文件中的接受请求媒体类型进行拓展,我们可以要求“Accept: application/x.myapi+json;version=2.0” 这样的方式来指定请求myapi的第二个版本。
对比第一种定制化字段的方式,这种模式利用了已有的HTTP头文件字段。比较好的适用于拥有默认版本选择的API,比如客户账户支付操作如果在不指定版本的情况下就走最严格的检查步骤(对应的版本),而一些特定机构可以走其它版本来简化步骤,以提高清算效率。
这种扩展已有字段的模式也存在比较明显的问题,这些字段的解析有可能已经被现有的客户端按照标准方式进行了解析和利用。比如针对媒体类型,很可能客户端进行了不同的缓存策略来提升用户体验,这样的改变将会让基于标准字段的客户端实现无法应用。
模式三:路由设计版本路径
通过HTTP定位API和Web资源都需要通过URI,很自然我们也可以在URI上加入版本信息,比如“/v2/myapi”,通过路由来识别请求的具体API版本。
如果一个API有多个差异较大版本,那么我们可能就会有“/v3/myapi”、“/v4/myapi”等一系列URI。从客户端视角,由于路由不同,所以其实可以认为每个版本都产生了一个新的API。
这种设计虽然保证了兼容性,但新的功能实际上并没有提供给已有使用者,想用新功能还是需要客户端的改变。而且新版本增加了API的数量,提高了整体运营成本。
模式四:请求参数版本字段
通过HTTP请求资源也允许在URL中带参数,利用这个特性我们可以传递希望请求的API版本,比如“/myapi?version=2”。
这种方式的可读性很好,也遵循HTTP的默认标准,对于API设计者来说比较容易实现,但存在实际运营过程中的挑战,特别是多层级API调用在路由过程中参数传递的问题,某种意义上会破坏兼容性。在一些通过API网关进行重定向的时候,由于路由系统不知道参数的意义,有可能从安全角度自动消除,造成请求真正到达服务侧时参数丢失。
以上四种常见模式实际上各自有不同的适用性场景,因此很难说API版本策略一定是采用哪种模式。比如当“破坏性”改变发生在某一个业务实体之上时,采用显式版本路由来明确调用的API版本就可能是最佳方式;而仅仅是为客户端可以采用更好的缓存技术来提高用户体验的版本改进,可能采用头文件中的字段来标注版本就是合适的。
API版本演进策略
读到这里,大家可能会认为必须在发布第一个API版本之前定义版本控制策略,否则后续API改变将会面临巨大的挑战。事实却并非如此,能够真正帮助大家迎战API版本挑战的是策略上的灵活,根据具体业务上下文选择合适的版本模式。
延迟决策可能是件好事情
我们都清楚现在软件和系统所面临的业务不确定性,一开始很难预期接下来针对业务实体的变化,比如当下没有人可以预言未来两年数字货币带来的新需求和新功能是什么样子的。而驾驭这样的不确定性其中的一条黄金定律就是“延迟决策”,在遵循了良好开放API设计规范的基础之上,形成良好的业务和技术合作,就能够针对每个开放API保留未来改变的灵活性。
解耦每项API治理工作
开放API本身是一个复杂系统,这里我们仅仅是谈了版本管理的挑战,如果加入注册发现等机制,我们会发现确实会互相影响,进而让策略讨论变得异常复杂。我们的建议仍然是保持这些关注点的分离,解耦各项治理工作,不要在一项治理策略中引入针对另外几项的依赖,比如版本策略的工作必须依赖于注册机制采用某种特定模式。一旦开始关联这些工作,我们就很难设计出真正意义上简单的解决方案。而我们都知道在开放的互联网生态中,唯有简单才能够持久,唯有包容才能够生存。
来源:ThoughtWorks商业洞见
作者:肖然
每周四晚8点【冬哥有话说】线上直播,4月“DevOps之庖丁解牛”,拆解DevOps的工具及具体实战。公众号留言“回放”可查看往期直播视频
- 0401《数据库持续交付流水线分享与演示(Azure DevOps+Flyway)》
- 0408《持续交付中的版本管理与基于Azure DevOps扩展框架的插件开发》
- 时间待定《微服务,多团队协作中的API测试怎么做 - Pact契约测试》
- 时间待定《BoatHouse端到端流水线展示》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。