一、前言

  在软件行业快速发展的今天,传统的软件授权已经不能足以满足一个IT类的公司的发展。虽然在大部分公司里,它还是现金池的直接源头。但是在可遇见的未来,受摩尔根理论的失效、物联网的发展等影响,应用的架构会越来越趋于简单化,架构越来越倾向于分布式水平扩展,对外的服务提供也会越来越SaaS化。在这种大背景下,很多公司都开始提供所谓的开放平台。

  查阅各个大公司的开放平台,我们不难发现,都是Rest API,都是HTTP请求,响应报文都是大同小异的XML或者是JSON等众多雷同的特点。这是为什么呢?让我们唠唠API平台的那些事。

插播:推荐一个张善友大神的关于web api的专栏:http://www.cnblogs.com/shanyou/category/307401.html

rpc的专栏http://www.cnblogs.com/duanxz/p/3769872.html

二、定义

  查看历史,我们惊讶地发现,其实Rest的概念早在2000年就被人提出。用一句话描述它,就是用固定的URI和可变的参数访问某个服务,来完成一系列业务请求。

  1. 每一个URI代表一种资源;
  2. 客户端和服务器之间,传递这种资源的某种表现层;
  3. 客户端通过几个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

2.1 Rest API 格式

  Rest API,无论它的名字多么高大上,它本质还是一个HTTP请求,POST也好,GET也罢,都是不同的数据提交方式。所以,能够决定一个Rest API的也就:URI、参数、请求方式、请求头等。

  我们一般用URI来定义希望对外暴露的服务。结构基本类似 schema://yourCompanyDomain/rest/{version}/{application}/{someService}schema可以是http,也可以是httpsversion指的是你这个API的版本,application一般会指向底层的某个子系统,someService就是这个子系统对外提供的服务。当然,如果按照业务为边界划分,也可将业务维度相同但隶属于底层不同的系统的服务定义为一个application

  对于这种Rest请求,常见的响应结果就是XML或者是JSON形式,往往结果中会包含请求状态,和时间戳,业务系统响应结果。

  具体的格式约定,可以看底部的参考文献。

2.1.1 API的版本概念

  在 URI 的格式定义中,我们包含了version这个字段,这在早期,其实被认为是不优雅的方式。阮一峰有一篇文章就专门抨击这种设计,后面他又自己打脸说还是拼接version的好。(不知道是不是因为Github的设计缘故)

  API设计时常会考虑版本这个概念,无论是在URI还是在请求参数里面,至少有一个地方得指明含版本,为什么呢?

  这很简单,就和系统迭代一样,API也是快速迭代开发的。也许初期,你的老大脑袋一热,我们要上API,于是你们就加班加点做,设定了一版请求协议。当时你们想,写完就能赚钱,必然带来一堆问题,比如说,代码难以维护,功能单一。后面这个API的第二版,你们要基于它去做一些新的变更,比如请求参数多了,返回内容多了,必然就有了第二版。但是又不能影响现有的业务。这个API基本的URI没变,但是会同时存在两个版本,老用户继续请求旧版,没问题,你无需动旧版?有需求的新用户,你要请求新版才能提供服务。这样通过版本来区分了不同的服务,便于以后的升级和维护。(就像BAE那货可以毫无压力地废弃掉2.0的API)

  所以一开始设计API时,就要定义好版本。其次,版本化可以骗钱。我们可以满怀恶意地猜测,1.0为了抢用户,免费。2.0老牛逼了,你用的爽了,想更爽,付费。233333333

2.2 架构特点

  API平台的架构,其实和底层公司的业务系统架构有着密切的关系,基于一个好的系统架构写一个Rest API平台基本是水到渠成的。我们先从业务系统的架构变迁说起,再来分析上面的API平台。   

2.2.1 业务系统架构变迁

  基本的软件公司的业务系统,一开始都是单机,单节点,单库。慢慢随着业务量的增加。这个系统越来越复杂,机器性能越来越不能满足需求。于是,第一种可能,领导说,换机器。上一台牛逼机器。但是机器性能有限,越牛逼的机器价格越贵,有时候都能买3~5台现有配置机器。于是就有了方案二,三个臭皮匠赛过一个诸葛亮。单应用,多机器,多节点

  但是慢慢过了几年。你发现,这代码写的越来越屎,越来越复杂。基本上新来的开发要熟悉好几个月的业务。就代码因为快速上线。一堆坑,无法改。于是,大家现在都在做的事情,就是拆分。也就是现在常说的SOA。拆分,也有拆的好的,拆的不好的。不好的,就是一个大的恶心系统,变成了一堆恶心的小系统,互相调用,成一团乱码。小系统看似很好。但是某个不起眼的小系统。一挂,那么全部的系统都瘫了。这个时候这个万年不维护的小系统还找不到负责人,他么早就滚了。这就是拆分的技术欠债,你无法避免。

  那么,就谈到拆分的架构设计。其实这块分两个架构,技术架构和业务架构。技术架构,就是要分清技术系统和业务系统。技术系统也可能是一个业务系统,但它一定是一个通用的服务组件。它提供的服务无任何定制需求,就是纯简单服务。比如,发短信发邮件发微信的通知系统,它就是通知。你业务有何特殊需求,就在上面自己实现一个XXX通知系统,那么业务系统的拆分,才是最关键的。就是要划清边界

  这个边界问题很可怕,什么你该做,什么你不该做。每个系统的职责都要明确。不要你也实现一个他也实现一个,然后相互调。这种可怕性就是在两个服务都瘫痪的时候,完全都无法启用。最后你的系统架构变成了一个通用技术组件系统,完成各个基础服务,每个产品线,业务端,基于你的技术系统包装出业务定制化服务系统,然后最上层就是业务子系统。业务子系统组合在一起,就是一个大的业务系统,也叫服务化平台。

  这个时候,你需要做开放平台。暴露一套Restful API,就是水到渠成了。

  PS,实际的架构远比这个复杂,截图选自《大型网站系统与Java中间件实践》。

2.2.2 基于不同系统架构的 API 平台

1、演变

  初期的API平台往往是上图左侧那种,某个庞大的业务系统希望暴露一套API,于是大家就在这个系统上做,直接设计一套协议。但是,这样子带来的缺点十分明显,第一,它与业务联系太重,理想的API平台是通用的,不是只给你设计一个。第二,它不好扩展,每次变更都得和业务系统一同上线,糟糕的情况下代码还会影响原有正常的业务。第三,性能问题,理论上会降低原有应用的性能。

  这种情况下,如果应用部署了多台机器,多个节点,我们就可以独立出来。也就是右边所示的API Gateway,它做的事情本质上就是反向代理,将外部的请求校验完合法性之后反代至内部实际想要对外暴露服务的服务集群上。

  所以,这种场景下,API Gateway也就如名称所说,就是一个入口。实际的Rest API的东西还是建立在各个业务子系统上,只是只需要提供最简单的服务,无需考虑授权等东西。用户管理,API注册发布,调用统计等,均由API Gateway实现处理。对于想要快速上线的开发人员而言,实在是一个不错的福音。

  然而,当系统应用拆分到了SOA化之后,API的架构由有了新的变革,我们有了注册中心的概念。因为SOA化,所以每个业务子系统其实都有了对外的统一接口,有了ESB(注册中心)。实际的内部系统间请求也有了较好的路由、熔断等策略。

  在这种大背景下,API平台对外暴露的Rest API无需底层的业务专门开发了。直接使用现有的内部接口,选择性暴露即可。问题点就在于,如何根据定义的Rest API请求,实际模拟内部的RPC协议请求。

  某种程度上,这时候的API平台,已经不仅仅是HTTP Rest请求了。我们完全可以实现相同RPC协议的透传,比如你就是一个Hessian接口想对外暴露,我只需包上一层认证,直接注册于API Gateway,外部Hessian请求直接透传至内部子系统。

  在这个基础上的 Rest API 平台,才是灵活的,可扩展的,易于维护的。然而有得必有失,Mock请求必然会有性能上的损耗,但是这个架构的公司,已经不在乎钱了,上10台虚机,不够加呗。

2、特点

  1. C/S结构
  2. 无状态(API平台无需存储业务状态,只做认证和转发)
  3. 有缓存,API会对指定URI的请求转发做缓存,保证并发性,业务系统也对同样的请求针对性缓存。
  4. 结构分层,每层间无法直接访问。

API平台的背后,就是庞大的各个业务子系统。每个API,就相当于一个业务子系统。API平台要做的事,就非常清晰和简单。就是业务子系统注册发布API,对外部请求校验计费,模拟请求内部业务子系统,对子系统结果包装序列化为JSON返回。

2.3 交互流程

  上面是一个简单的交互,简单显示了外部系统和内部系统通过Rest API的交互过程:开发者(企业)注册,申请APP_KEY,开通API。按照开发接入,请求签名。转发至后端调用返回结果,API平台计费(预付费或者后收费),统计调用情况。

  外部通信本质上还是HTTP,那么必然存在了授权问题,生产的API平台是直接暴露于公网的,如果认证授权策略出现纰漏,影响是可怕的。

  比如,这个API是群发短信,你要是没有好的授权体系,允许人随意推送。某个人想搞你,调用发布反共信息,你整个公司都会跨。HTTP协议本质上没有这一块的内容,所以我们必然要在这上面考虑安全策略的内容。

2.3.1 如何保证Rest API的安全性

  如果单纯考虑加解密,或者签名方式来保证请求合法,其实是远远不够的。事实上,一个安全的API平台往往需要多方面一起考虑,保证请求安全合法。

1、是不是实际客户端的请求?

  1. 设计专门的私有请求头:定义独有的Request headers,标明有此请求头的请求合法。
  2. 请求包含请求时间:定义时间,防止中间拦截篡改,只对指定超时范围内(如10秒)的请求予以响应。
  3. 请求URI是否合法:此URI是否在API平台注册?防止伪造URI攻击
  4. 请求是否包含不允许的参数定义:请求此版本的这个URI是否允许某些字段,防止注入工具。
  5. 部分竞争资源是否包含调用时版本(Etag):部分竞争资源,使用If-Match头提供。如用户资金账户查询API,可以返回此时的账户版本,修改扣款时附加版本号(类似乐观锁设计)。

2、API平台是否允许你调用(访问控制)?

  访问控制,主要是授权调用部分。API都对外暴露,但是某些公共API可以直接请求,某些,需要授权请求。本质的目的,都是为了验证发起用户合法,且对用户能标识统计计费。

  以HMac Auth为例,我们简单设计一个签名算法。开发者注册时获取App Key、App Secret,然后申请部分API的访问权限,发起请求时:

  1. 所有请求参数按第一个字符升序排序(先字母后数字),如第一个相同,则看第二个,依次顺延。
  2. 按请求参数名及参数值相互连接组成一个字符串。param1=value1&param2=value2...(其中包含App Key参数)
  3. 将应用密钥分别添加到以上请求参数串的头部和尾部:secret + 请求参数字符串 + secret。
  4. 对该字符串进行 SHA1 运算,得到一个二进制数组。
  5. 将该二进制数组转换为十六进制的字符串,该字符串为此次请求的签名。
  6. 该签名值使用sign系统级参数一起和其它请求参数一起发送给API平台。

服务端先验证是不是实际客户端的请求,然后按照App Key查找对应App Secret,执行签名算法,比较签名是否一致。签名一致后查看此App Key对应的用户是否有访问此API的权限,有则放行。

执行成功后包装返回指定格式的结果,进行统计计费。

三、需求与实现

3.1 系统需求

3.1.1 目标

  1. 支持rest类API接口动态发布及运营,包括但不限于:

    • 安全认证
    • 会话管理
    • 流量统计及限流
    • 计费收费
    • 熔断
  2. 支持现有子系统RPC协议的API动态发布及运营,外部请求透传。
  3. 支持json、xml响应报文,可以请求时选取所需报文格式。
  4. 支持动态直接将后端SOA服务暴露为API。
  5. 支持动态将普通Web接口暴露为API。
  6. 支持动态将MQ服务暴露为API。
  7. 支持多个服务组合编排后暴露为API。

3.1.2 业务需求

1、API管理

所有API可后台查询管理,包括动态发布、参数映射配置、后端服务接口配置、API禁用、启用,多版本、分组、分级别等。

2、应用管理

后台管理开放平台接入的应用(第三方应用),包括查询、禁用、启用、审核。

3、API鉴权&授权

  1. 应用申请审核通过后生成公钥,开放平台需提供支持分布式系统的密钥管理
  2. 服务可设置为两个安全等级:需授权访问和无需授权访问(后者即任意客户端都可以发起调用),默认所有API都需授权访问。
  3. 非正常状态(禁用、停用、黑名单等)的应用直接抛异常不允许访问——熔断机制
    • 调用次数、调用频率、并发数可运行时控制,避免某请求量过大影响其他应用的调用。
    • 可对某个应用某个API设置强制熔断,所有请求无视阀值直接抛出异常。
  4. 易用性
    • 与SOA集成,SOA服务一键发布到API平台。
    • 支持后台动态发布API,而不是新上一个API就需上线一次。

4、计费统计

  1. API的调用统计,每笔请求时间,响应时间,响应状态。
  2. API的计费计算,按照请求量和请求资源计费,实现多种计费模型。(预付费,后收费。按量,按时间周期。)

5、开发者平台

  1. API开发者平台,开发者注册、访问、申请API授权、计费统计、调用统计。
  2. API文档系统,详细的API文档展示,SDK下载,用户登录后还可专门生成不同编程语言请求,在线模拟请求结果等。

3.1.2 角色定义

1、外部用户

用户 做什么 使用目的
API平台接入方 接入API平台 使用XXXX提供的开放平台服务

2、各个业务产品线

用户 做什么 使用目的
各个业务产品线 作为外部应用接入API平台 使用XXXX提供的开放平台服务
各个业务产品线 提供服务 提供后端服务,发布到API平台供外部应用接入
公司后端应用 提供服务 提供后端服务,发布到API平台供外部应用接入
API平台 API治理 运营,管理API、第三方应用等

3.2 请求模型

  API 的所有服务请求域名是相同的,区别在于Request Path等。请求参数分为系统级参数和业务级参数两部分,系统级参数是所有 API 都拥有的参数,而业务级参数由具体服务 API 定义。

3.2.1 统一服务 URL

  建立API Gateway接受所有请求,按照Request Path,Request Method,Request Head分发所有的请求。

1、 通用统一URL

格式:schema://<API Gateway URI>/DispatcherServlet?method=XXService.xxMethod?xxxObj.xxxParam=xxxValue。

说明:所有请求直接走DispatcherServlet分发,所有内容均定义于URL参数中。method为后端某个子系统的某个方法。xxxObj.xxxParam为方法参数实体的某个属性的值定义。

示例: http://api.xxxxx.com/router?method=SMSService.sendSMS&user.phoneNumber=18888888888&sign=ds234324sdsad&date=20151229231232

2、Rest类型URL

格式:schema://<API Gateway URI>/rest/{version}/{service}/{method}/{params}

说明:请求按照Gateway定义的Rest地址匹配,动态映射至具体系统具体方法,模拟调用。请求中包含version字段。

示例: http://api.xxxx.com/rest/v1/XXService/xxMethod/{xxParam}http://api.xxxx.com/rest/v1/XXService/xxMethod?xxxParam=xxxValue

3.2.2 参数设计

1、系统级参数

  系统级参数是由 API 平台定义的一组参数,每个服务都拥有这些参数,用以传送框架级的参数信息。如我们前面提到的 method 就是一个系统级参数,使用该参数指定服务的名称。

2、业务级参数

  业务级参数,顾名思义是由业务逻辑需要自行定义的,每个服务 API 都可以定义若干个自己的业务级参数。API Getaway 根据参数名和请求属性名相等的契约,将业务级参数具体的方法请求对象中。

3.3 常见框架

  1. Kong:https://github.com/Mashape/kong
  2. Zuul:https://github.com/Netflix/zuul
  3. ROP:https://github.com/itstamen/rop

四、优劣

4.1 好处

  1. 跨平台,管你是Java,还是PHP,还是Node.js还是Go,你丫都得支持HTTP请求。我API平台只需要提供这个语言的SDK,保证能按照消息协议调用就好。

  2. 将复杂的内部业务系统抽象为通用调用请求。包装了复杂的业务逻辑,对外提供统一的,好管理的接口。并可以定制化设计,计费,授权一类的容易管理。

4.2 坏处

  1. 协议描述能力弱化,RestfulURI无法完全对请求参数做强格式校验。最后的方法参数绑定,模拟内部请求时往往容易出问题,尤其是以Java等强格式语言的系统。不能像WebService一样清晰描述请求报文。

  2. 同样的道理,响应结果为了是JSONXML。这当中,编码,正反序列化,等操作,往往就会有性能瓶颈。而且,Java在这块资源消耗极大。以Github的ROP这个框架为例,当年测试时,它在并发请求过高的时候就会有一个内存泄漏问题。


附:参考资料

  1. REST Is Not About APIs, Part 1
  2. REST Is Not About APIs, Part 2
  3. RESTful API 设计指南
  4. 理解RESTful架构
  5. 撰写合格的REST API
  6. Netflix 官网

Rest API的更多相关文章

  1. 干货来袭-整套完整安全的API接口解决方案

    在各种手机APP泛滥的现在,背后都有同样泛滥的API接口在支撑,其中鱼龙混杂,直接裸奔的WEB API大量存在,安全性令人堪优 在以前WEB API概念没有很普及的时候,都采用自已定义的接口和结构,对 ...

  2. 12306官方火车票Api接口

    2017,现在已进入春运期间,真的是一票难求,深有体会.各种购票抢票软件应运而生,也有购买加速包提高抢票几率,可以理解为变相的黄牛.对于技术人员,虽然写一个抢票软件还是比较难的,但是还是简单看看123 ...

  3. 几个有趣的WEB设备API(二)

    浏览器和设备之间还有很多有趣的接口, 1.屏幕朝向接口 浏览器有两种方法来监听屏幕朝向,看是横屏还是竖屏. (1)使用css媒体查询的方法 /* 竖屏 */ @media screen and (or ...

  4. html5 canvas常用api总结(三)--图像变换API

    canvas的图像变换api,可以帮助我们更加方便的绘画出一些酷炫的效果,也可以用来制作动画.接下来将总结一下canvas的变换方法,文末有一个例子来更加深刻的了解和利用这几个api. 1.画布旋转a ...

  5. JavaScript 对数据处理的5个API

    JavaScript对数据处理包括向上取整.向下取整.四舍五入.固定精度和固定长度5种方式,分别对应ceil,floor,round,toFixed,toPrecision等5个API,本文将对这5个 ...

  6. ES5对Array增强的9个API

    为了更方便的对Array进行操作,ES5规范在Array的原型上新增了9个方法,分别是forEach.filter.map.reduce.reduceRight.some.every.indexOf ...

  7. javascript的api设计原则

    前言 本篇博文来自一次公司内部的前端分享,从多个方面讨论了在设计接口时遵循的原则,总共包含了七个大块.系卤煮自己总结的一些经验和教训.本篇博文同时也参考了其他一些文章,相关地址会在后面贴出来.很难做到 ...

  8. 一百元的智能家居——Asp.Net Mvc Api+讯飞语音+Android+Arduino

    大半夜的,先说些废话提提神 如今智能家居已经不再停留在概念阶段,高大上的科技公司都已经推出了自己的部分或全套的智能家居解决方案,不过就目前的现状而言,大多还停留在展厅阶段,还没有广泛的推广起来,有人说 ...

  9. 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用

    由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...

  10. bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序

    也许单页程序(Single Page Application)并不是什么时髦的玩意,像Gmail在很早之前就已经在使用这种模式.通常的说法是它通过避免页面刷新大大提高了网站的响应性,像操作桌面应用程序 ...

随机推荐

  1. Tomcat7 JDK8 Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000540000000, 5368709120, 0) failed; error='Cannot allocate memory' (errno=12)

    [root@crm-web- bin]# shutdown.sh bash: shutdown.sh: command not found [root@crm-web- bin]# sh shutdo ...

  2. [蓝桥杯]2017蓝桥省赛B组题目及详解

    /*——————————————————————————————————————————————————————————— [结果填空题]T1 (分值:5) 题目:购物单 小明刚刚找到工作,老板人很好 ...

  3. java 锁 Lock接口详解

    一:java.util.concurrent.locks包下常用的类与接口(lock是jdk 1.5后新增的) (1)Lock和ReadWriteLock是两大锁的根接口,Lock代表实现类是Reen ...

  4. .net使用SqlBulkCopy类操作DataTable批量插入数据库数据,然后分页查询坑

    在使用SqlBulkCopy类操作DataTable批量插入数据,这种操作插入数据的效率很高,就会导致每一条数据在保存的时间基本一样,在我们分页查询添加的数据是,使用数据的添加时间来排序就会出现每页的 ...

  5. python读取导出数据

    1,python读取csv的某一列 import pandas as pd data1 = pd.read_csv('cotton.csv', usecols=[0, 1], encoding='ut ...

  6. 最简单的JAVA解析XML字符串方法

    引入 dom4j 包<dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifa ...

  7. 题解:YNOI/GZOI2019 与或和

    题目大意: 1. 求所有的子矩阵的and之和2. 求所有子矩阵的or之和 由于是位运算,那么久直接拆位,于是就变成了求全0子矩阵的个数和全1子矩阵的个数那么题目就变成了简单的单调栈问题 #includ ...

  8. redisson整合spring

    转: redisson整合spring 转: 原文:http://blog.csdn.net/wang_keng/article/details/73549274 首先讲下什么是Redisson:Re ...

  9. maven的安装教程

    一.准备工作  1.确定电脑上已经成功安装jdk7.0以上版本                 2.win10操作系统                 3.maven安装包            下载 ...

  10. (七) UVC框架分析

    title: UVC框架分析 date: 2019/4/23 19:50:00 toc: true --- UVC框架分析 源码的位置在drivers\media\video\uvc,查看下Makef ...