Rest API

 

 

 

 

1、前言

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

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

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

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

2、定义算法

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

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

2.1 Rest API 格式

  Rest API,不管它的名字多么高大上,它本质仍是一个HTTP请求,POST也好,GET也罢,都是不一样的数据提交方式。因此,可以决定一个Rest API的也就:URI、参数、请求方式、请求头等。json

  咱们通常用URI来定义但愿对外暴露的服务。结构基本相似 schema://yourCompanyDomain/rest/{version}/{application}/{someService}schema能够是http,也能够是httpsversion指的是你这个API的版本,application通常会指向底层的某个子系统,someService就是这个子系统对外提供的服务。固然,若是按照业务为边界划分,也可将业务维度相同但隶属于底层不一样的系统的服务定义为一个application后端

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

  具体的格式约定,能够看底部的参考文献。数组

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 平台

一、演变

 

  初期的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台虚机,不够加呗。

二、特色

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

二、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、需求与实现

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 业务需求

一、API管理

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

二、应用管理

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

三、API鉴权&受权

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

四、计费统计

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

五、开发者平台

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

3.1.2 角色定义

一、外部用户

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

二、各个业务产品线

用户 作什么 使用目的
各个业务产品线 做为外部应用接入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分发全部的请求。

一、 通用统一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

二、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 参数设计

一、系统级参数

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

二、业务级参数

  业务级参数,顾名思义是由业务逻辑须要自行定义的,每一个服务 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、优劣

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 官网
相关文章
相关标签/搜索