掌握聚合最新动态了解行业最新趋势
API接口,开发服务,免费咨询服务

Etsy的API First转变——并发性

 Etsy内部一直在对Web API进行各种新的尝试。我们切换到了API First设计上,在组合层上采用了并发性的处理方式,在API设计上引入了强类型,尝试了代码生成,还未API开发了分布式追踪工具。

 我们在这个过程中遇到了一个普遍的问题:我们大都数的逻辑都被实现了两次。我们所有的代码都是针对网站所写的,之后他们必须要被重新整合到API中,才能被iOS和安卓应用所使用。


问题:在平台间重复逻辑

 我们想要采用这样一种方式:所有东西都是在可重复使用的API组件上打造的,这些组件可以在web和应用中分享。可惜的是,我们现存的API框架无法支持这种方式。解决方式就是放弃当前的框架,并且进行重构。

 因此我们搭建了一个API First架构,在这个架构下,所有功能上的变化都会优先在API层面上表达,然后在被整合进网站中。

  •  第一个问题:更多的设备与平台(还有JavaScript

 未来的世界将会充满越来越多的设备。纵观历史,主机变成了桌面型电脑,桌面型电脑又变成了笔记本电脑,然后平板电脑、智能手机、智能手表随之而来。

 这个趋势已经延续了很长一段时间,并且还将继续延续下去,为了避免每出现一个新设备就要重新来过的情况,在几年前,我们开始使用内部API来分享数据。

  •  第二个问题:性能&复杂性控制

 就像权利与业务相辅相成一样,API也有其缺点。服务器代码很简单,但是我们没有意识到即将到来的各种参数。服务器端的性能受到了影响。而且衡量性能也非常困难,因为很难知道,增加的响应时间,是受到后端性能的影响,还是受到了客户端请求更多资源的影响。

  •  第三个问题:重复与矛盾

 多年以来,模式的变更以及MVC架构数据库变得越来越复杂,让我们养成了坏习惯:在模板渲染的时候进行数据取回,以及模板中的罗技。例如我们针对AjAX开发的API,它的后端为PHP语言。我们没有在同一个地方设定一个可以被WebAPI都能够重复使用的逻辑。这导致了APIpre-API Web之间的前后矛盾。

 API First的要求


 我们重新讨论了API的要求。如果新能遇到了问题,瓶颈在哪里?

 首先,time to glass,也就是需要多长时间我们才能在设备的屏幕上看到内容,Ilya Grigorik指出,由于移动网络速度的限制,我们在服务器端只有100毫秒的时间。第二,以Etsy举例,我们来自一个“序列什么也分享不了的php世界”。没有内置的并发性。我们如何让工作完成平行,并且可重复使用,同时还要维持较低的网络足迹?


API v2:在平台间重复逻辑                       



API v3:可重复使用的组件

 API First的其他需求,还包括如何考虑缓存。前一个版本的APImemcached only,缓存调用包括参数,这会导致粒度问题。最后一个问题,就是我们要用一个我们已经熟知,而且已经擅长的方式去解决问题:用PHP创建自己的解决方式。

 准备工作


 基于上述因素,我们一点一点的确定了一个新的架构,它被称API Version 3REST资源可以良好的兼容移动应用与传统Web,因此它需要被保留。我们提出了一个新的概念,那就是将endpoint从储存他们的框架中减弱。将endpoint的职责进行最小化,让它只负责: 

  •   声明路径
  •   声明输入期望与输出保障
  •   实施endpoint中发生的事情

 我们给每个endpoint都准备了一个简单、具有陈述性的文件。

 其他所有的功能都被有目的的移除了:StatsD错误监控、endpoint输入和输出类别检查,以及完整路径的编辑——所有这一切现在都由框架所处理。认证和访问控制也在这里被处理,基于开发者所选择的endpoint的类。

  •  进入meta-endpoint

 我们借鉴了Netflix和eBay的ql.io的经验:允许第二个层面的endpoint,也就是我们自己API的使用者,访问和聚集其他endpoint。这意味着,服务器本身也是API的客户端,让服务器更加复杂,同时给它赋予了更多的控制权,去控制额外层面的代码执行。此举改善了客户端的性能,因为它只需要一条请求——如果我们想要一个响应式的移动界面,请求的数量就是其最大的瓶颈。

 这些请求使用我们生成的PHP客户端以及cURL。何为cURL?我们会在下面介绍。

  •  cURL

 假设我们现在在使用HTTP,那么如何让传统的HTTP请求具有并发性?我们发现可以用cURL实现这个目的。

 2013年,Paul Wright在Twitter中表示:

 

在某个项目中,Paul和Etsy的核心团队成员Matt发现,我们可以在HTTP层中搭建并发性,方法就是通过平行的cURL来调用curl_multi_info。

除了cURL之外,我们还添加了一个逻辑,来建立请求和其他endpoint之间的依赖,我们将其称为代理(proxy)。当响应的代理变成unblocked的时候,我们就会硬性请求,NodeJS
NodeJS*中的Event Loop类似。整个变发型以来分析和安排都是包含在一个软件内的,我们将其称为curl回调协调器(curl callback orchestrator)。


这个方法非常好用,因为从endpoint作者的观点来看,代码看上去非常有序,而且是single-threaded的,它只是对其他endpoint的代理调用的列表。这个协调器能够分析出如何安排这些调用,这让我们离目标又近了一步。

不需要重新部署API


好的,我们现在已经很好的观察了以前版本的API,也有了cURL,它可以帮我们解决很多问题。

下一步就要创建一个全新的API框架了。

视角与服务


Etsy的API v3中,有两个非常重要的概念:视角与服务。

视角规定了数据获取的规则,并且给我们提供了安全的提示:哪个视角可以获取哪些代码。他们表达了API的调用是谁所发起的。举个例子,Public视角显示的是,已经登出的用户可以在Etsy.com上看到的数据。


Member视角则是只有登录用户才能看到的数据。Shop视角与Member视角类似,但是只应用于商店中。Admin视角针对的是Etsy管理员。

而服务表示的,则是调用是在哪里发起的。服务也可以被看做是API框架的切入点。每一个服务都有自己在验证方面的需求。Endpoint在默认情况下被包含于一些服务当中。其他服务则是opt-in的,每一个endpoint都需要声明它是否想要暴露在opt-in服务中。


Ajax服务可以在etsy.com上运行JavaScript的页面中获取。Admin服务可以在内部管理员工具平台上运行JavaScript的页面上获取。Internal服务可以被已经加入了我们的API cluster网络的其他API服务所使用。Apps服务可以在iOS和安卓应用中使用。3rd party服务是针对第三方应用开发者所开发的。不同的服务区分了不同的程序域名。

API调用实例


现在我们来看一个例子。我们都知道etsy.com主页的样子:作为一名潜在买家,主页上都是我可能感兴趣的信息。页面最顶部列出了我以前收藏的东西,然后是算法根据我的喜好推荐的东西,我收藏的店铺的上新、好友最近的活动等。基本上就是这个样子:


如果更仔细的查看其中的细节,我们会发现它的架构其实非常整齐,像是一棵树,从左向右生长。

我们的网络和服务器反应了API调用的结构,它开始于一个HTTP请求(从浏览器到Etsy的Web服务器)。之后,API服务器会收到一个API请求,请求一个个性化版本的主页数据。在内部,这个请求包含了多个并发性的组件。他们本身通过API请求所获取。例如我的收藏,他们就是并发性组件,因为他们是大量的可以被平行取回的listing card。

因此,我们可以将API请求想象成一个有着多个层面的树,开始其他的API处理,并且从那些自请求的结果中构建一个总体结果。

API endpoint的领域特定语言


让我真正开始深入了解Etsy的API v3的项目,是统一API endpoint的语法。这是一件非常有意思的事情,进而让我通过自动化的方式统一了API的代码库。过去,我们有着多种endpoint书写方式。为了将它们进行统一,我们创造出了一种endpoint模块化语言。

一些模块是每一个endpoint都必须要使用的。每一个endpoint都需要声明它的路径,这样我们才能知道在哪里可以找到它。另外,它还需要一个人类可以看懂的描述,以及一个resultType。

 

这个resultType描述了endpoint会返回哪种类型的数据。所有所返回的数据,都被编译成了JSON,但是在这里我们可以说我们返回了一个原始的数据类型,例如在编译中包含了字符串或是boolean。否则,我们可以返回一个所谓的“typed resource”——也就是一个符合类型,它用来表示Etsy程序领域的特定组件,例如ListingCard。

还有句柄函数。在这个函数内,每一个endpoint都运行着它所需要的代码,从而生成它的反馈。


API endpoint还支持可选模块。只有在endpoint的确需要输入参数的情况下,declareInput才会发挥作用。否则,这个函数就会被忽略。

includedServices函数允许endpoint选择是否使用某个服务。例如,TtsyApps服务就是可选的,如果你想要让你的额endpoint在应用上可用,你就必须使用这个函数来包括EtsyApps服务。

还有cacheTtlSeconds函数,它让你可以规定一个endpoint是否要被加入缓存,以及在缓存中保留的时间。

输入与输出


当请求被route到endpoint的时候,第一步就是设置输入参数。我们会根据请求的URL和endpoint的declareInput函数来创建输入对象。

输入声明告诉我们如何检查可选或强制输入参数,如果漏掉了一个参数,或是参数的类型错误,框架会返回一个HTTP错误和相应的提示信息。输入生命给每一个参数都制定了类型,例如字符串或是user ID。这些类型都是Etsy特有的,每一个类型都有自己的验证函数,这些验证函数由框架所运行。

每一个endpoint会通过resultType函数来定义自己的输出类型。目前,这些类别都是可选的。我们鼓励开发者要么返回原始数据类别,要么打造一个复合类别。

为了完成这个框架,我们还需要做一些事情。如果将一个API请求route到endpoint上?如何从代码中生成一个API请求,例如当网站使用AJAX的时候,如何在mete-endpoint或是JavaScript内发起请求?

API编译器


我们还需要两个软件,我们可以根据endpoint声明文件来进行自动编译。这就是API编译器的作用。起初,这是一段从endpoint声明中获取路径的脚本,然后综合服务和视角信息,通过修改.htaccess文件,将它们编译成一个完成的路径。

随着时间的推移,我们还发现了它的另一个作用:在PHP和JavaScript中生成API客户端代码。它会使用mustache模板生成代码,虽然这是一个针对网站的模板语言,但是在这个情境下也能良好的发挥作用。在将代码部署到Etsy.com之前,我们用Jenkins检查了编译出来的路径和客户端代码是否为最新。这样做的目的,在于我们能够控制API堆栈的两端。最终我们将其运用到了实际的工作中。

原文来自:SDK.cn

声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com

  • 营运车判定查询

    输入车牌号码或车架号,判定是否属于营运车辆。

    输入车牌号码或车架号,判定是否属于营运车辆。

  • 名下车辆数量查询

    根据身份证号码/统一社会信用代码查询名下车辆数量。

    根据身份证号码/统一社会信用代码查询名下车辆数量。

  • 车辆理赔情况查询

    根据身份证号码/社会统一信用代码/车架号/车牌号,查询车辆是否有理赔情况。

    根据身份证号码/社会统一信用代码/车架号/车牌号,查询车辆是否有理赔情况。

  • 车辆过户次数查询

    根据身份证号码/社会统一信用代码/车牌号/车架号,查询车辆的过户次数信息。

    根据身份证号码/社会统一信用代码/车牌号/车架号,查询车辆的过户次数信息。

  • 风险人员分值

    根据姓名和身份证查询风险人员分值。

    根据姓名和身份证查询风险人员分值。

0512-88869195
数 据 驱 动 未 来
Data Drives The Future