API 开发中如何使用限速应对大规模访问

标签: dev | 发表时间:2018-11-13 00:00 | 作者:
出处:http://itindex.net/relian

编者注:俗话说的好 “并发不够,机器来凑”,当我们面对高并发请求的时候增加机器是最简单也是最土豪的做法。不过在资源有限的情况除了去优化代码我们又该怎么办呢?今天我们请来了 @有马同学为我们分享一下他在这方面的经验,希望能帮助到大家。


API开发中如何使用限速应对大规模访问

想要开发牢固的Web API只考虑安全是不够的,还有一点我们需要考虑,那就是应对大规模访问的对策。不仅是Web API服务,任何在网络上公开的服务都会时不时地遇到来自外部的大规模访问,比如“鹿晗关晓彤公布恋情”这种实时热点。当服务器遇到大规模访问时,为了处理这些访问会耗尽资源,进而无法提供服务。这时不仅是这些大规模访问,任何人都无法和服务器端建立连接。

我们可以通过程序毫不费力的访问Web API,所以API服务器更容易遇到访问负载高的情况,针对这个问题,和普通的Web应用一样,我们可以对API服务进行扩容,这是正确的做法,但本文不对扩容方案展开讨论。接下来会讨论限速在应对大规模访问时一些重要的点,以及在ThinkJS开发的项目中应该怎样做。

限制用户的访问

为了解决突然出现大规模访问的问题,最现实的方法是对每个用户的访问次数进行限制。也就是确定单个用户在单位时间里最大的访问次数,如果用户已经超过了最大访问次数,用户再次访问时,服务端将会直接拒绝并返回错误信息。比如设置一个用户10分钟内只允许调用20次获取短信验证码的接口,那么当用户在10分钟内发起第21次请求时,服务器端便会返回错误信息,10分钟之后才会恢复访问。如果进行访问限速,就要先解决下面三个问题:

  • 如何确定限速的数值
  • 如何确定限速时间单位
  • 在什么时候重置限速的数值

确定限速数值

对数据频繁更新的查询类API而言,用户需要频繁的访问的到最新的数据,如果设置1小时只能访问10次的话,用户肯定不满意,转而去用可以替代的服务。访问限速的初衷是为了应对服务器短时间内遭遇大规模访问不堪重负从而无法提供服务,但如果让用户用起来不方便就得不偿失了,所以要尽可能的了解提供的API在什么情况下被使用,然后决定限速的数值。

确定限速时间单位

根据在线服务的不同,有些会以一天作为访问次数的时间单位,不过这对很多API来说有点长了,假设使用者正在写脚本访问API,开始并不清楚访问次数的时间单位,那就可能需要让他等24个小时才能继续访问API,或者换一个账号。如果我们以10分钟作为访问次数的时间单位,如果超出访问次数限制,也只需要等10分钟就能继续访问了。虽然单位时间的设定和API返回的数据密切相关,但大部分已公开的API都设置了都设置了1小时左右的单位时间。

确定重置限速数值的时间

当用户超出访问上限值时,服务端该如何返回响应消息呢?这种情况下可以返回HTTP协议中备好的“429 Too Many Request”状态码。429状态码在2012年4月发布的RFC 6585中定义,当特定用户在一定时间内发起的请求次数过多时,服务器端可以返回该状态码表示出错。RFC 文档中对该状态码描述如下:

    429 Too Many Requests

   The 429 status code indicates that the user has sent too many
   requests in a given amount of time ("rate limiting").

   The response representations SHOULD include details explaining the
   condition, and MAY include a Retry-After header indicating how long

通过上面的描述可以知道,响应消息中应该包含错误的详细信息,并且可以通过 Retry-After告知用户需要等待多长时间才能访问API。 Retry-After首部表示客户端需要等待多长时间才能再次访问。RFC文档中用 MAY标记该首部,表示即使不发送该首部也不会有什么问题,只是在响应体加上该首部会显得更加友好。

另外, Retry-After并不是 429 状态码专用的响应首部。该首部在HTTP 1.1的RFC 7231中定义,它也同样包含在带有503和3xx系列的响应体中。而且 Retry-After首部用秒数来指定时间,还可以使用详细的日期信息,可以看一下RFC文档中的描述:

    Retry-After

   Servers send the "Retry-After" header field to indicate how long the
   user agent ought to wait before making a follow-up request.  When
   sent with a 503 (Service Unavailable) response, Retry-After indicates
   how long the service is expected to be unavailable to the client.
   When sent with any 3xx (Redirection) response, Retry-After indicates
   the minimum time that the user agent is asked to wait before issuing
   the redirected request.

   The value of this field can be either an HTTP-date or a number of
   seconds to delay after the response is received.

     Retry-After = HTTP-date / delay-seconds

   A delay-seconds value is a non-negative decimal integer, representing
   time in seconds.

     delay-seconds  = 1*DIGIT

通过HTTP响应传递限速信息

在实施访问限速的过程中,如果能将当前用户访问次数限制、已使用的访问次数以及何时重置访问限速等信息告诉用户,会显得非常友好。如果不返回这些信息的话,用户可能为了确定限速是否解除而多次尝试访问接口API,这样一来无疑又增加了服务器的压力。

限速信息可以放在响应消息首部,另一种是作为响应消息体数据的一部分,目前将限速信息放在响应消息首部的方式成为事实上的标准。

看一下 GitHub的限速策略,GitHub就使用了上面三个响应首部,没有带 Retry-After首部。对于认证的请求每小时可以访问5000次,没有认证的请求每小时访问60次。

Twitter限速策略的时间窗口是15分钟,比GitHub的时间窗口小很多,因为Twitter的数据更新的相对较较快,时间窗口设置小一些才能满足使用者获取最新数据的需求。Twitter使用类似上面三个的响应首部传达限速信息 x-rate-limit-limit, x-rate-limit-remaining, x-rate-limit-reset。对于GET请求有两种初始方案,一种是15分钟15次请求,另一种是15分钟180次请求,并且只允许认证访问。

通过对比GitHub和Twitter的限速策略,可以知道只要准确传达限速信息,响应头部完全可以自己定义,重点是语义明确,且不能和其他标准首部冲突。

在ThinkJS中实现API限速控制

要实现API访问限速,需要对每个用户及应用访问API的次数进行计数,一般会使用 Redis等键值对存储来记录。ThinkJS 结合自己的路由映射方式实现了 think-ratelimiter中间件对 action进行限速,你需要在 middleware.js里进行如下配置,就可以实现简单的限速策略。

    // in middleware.jsconstredis=require('redis');const{port,host,password}=think.config('redis');constdb=redis.createClient(port,host,{password});constratelimiter=require('think-ratelimiter');module.exports={// after router middleware{handle:ratelimiter,options:{db,errorMessage:'Sometimes You Just Have To Slow Down',headers:{remaining:'X-RateLimit-Remaining',reset:'X-RateLimit-Reset',total:'X-RateLimit-Limit'},resources:{'test/test':{// key 是 controller/action 的拼接id:ctx=>ctx.ip,max:5,duration:7000// ms}}}},}

响应体首部 X-RateLimit-Reset表示可以恢复访问的时间,同时也会带着 Retry-After首部,它的值是距离恢复时间的秒数。

总结

在ThinkJS开发的Web应用中,可以使用中间件然后添加配置实现简单的限速,如果你提供的web API服务访问量比较大或者需要付费访问等功能,就需要在真正的逻辑前加一层来做限速相关的事情,在ThinkJS中可以实现一个 services/ratelimit.js,然后在项目的 base controller中实现限速等逻辑。

参考资料:

相关 [api 开发 限速] 推荐:

API 开发中如何使用限速应对大规模访问

- - IT瘾-dev
编者注:俗话说的好 “并发不够,机器来凑”,当我们面对高并发请求的时候增加机器是最简单也是最土豪的做法. 不过在资源有限的情况除了去优化代码我们又该怎么办呢. 今天我们请来了 @有马同学为我们分享一下他在这方面的经验,希望能帮助到大家. API开发中如何使用限速应对大规模访问. 想要开发牢固的Web API只考虑安全是不够的,还有一点我们需要考虑,那就是应对大规模访问的对策.

PHP API 框架开发的学习

- 郑小东 - 标点符
基于互联网的应用正变得越来越普及,在这个过程中,有更多的站点将自身的资源开放给开发者来调用. 对外提供的API 调用使得站点之间的内容关联性更强,同时这些开放的平台也为用户、开发者和中小网站带来了更大的价值. 在开发API前,你需要的是给API设定一个框架,这个框架一定是要简单的且是容易扩展的. 下面就是用就来看看如何使用PHP来创建一个API.

Google API 和开发者产品元素表

- 阿贡 - ITeye资讯频道
Google这只巨鳄实在是太巨大了,其拥有庞大的产品线,而每个产品线几乎又有无数为开发者提供的API,如果你想开发第三方应用就可以直接使用,但Google到底有多少API呢. 为了搞清楚这个问题,Google自己制作了一个“]Google API 和开发者产品元素表”,里面列出了截止2011年1月为止的所有API和跟开发者有关的产品,涵盖移动、搜索、gadgets、数据API、社会化、广告、地图、工具、Chrome等等方面,点击每个“元素”即可了解更多信息.

用Python开发可用于iPhone的Google Reader API

- MadFrog - 乱弹琵琶playit
Google Reader是我最喜欢的web服务,可惜在iPhone上没有一个我理想中的客户端,所以只好自己动手丰衣足食了. 而开发的第一步自然就是搞定Google Reader API,可惜Google一直没有放出官方文档. 所幸的是前人已经通过反向工程探寻出了相关信息(GoogleReaderAPI、Using the Google Reader API和GReader-Cocoa等),所以不用自己去一一摸索了.

Flickr向开发者提供实时Web API

- 品味视界 - cnBeta.COM
Flickr今天向程序员推出了一个名叫Photo Hack Day的活动,释放了一批新的代码和API,让开发人员能够利用Flickr实时访问数据库从而构建新的应用,目前Flickr已经保有60亿张以上的图像. 新的API可以搜索每天上传的数以百万计照片并实时提供,这些数据还包含50多个机构例如美国宇航局,美国国会图书馆,美国和英国国家档案馆的更新.

HTC 将向开发者提供 Beats OpenSense 的音效 API 接口

- SINCERE - Engadget 中国版
在 8 月份的时候,HTC 和 Beats Electronics 走在了一起,之后我们看到了搭配 Beats Audio 技术的机型推出,在这之前,HTC 提供了众多的 SDK 给开发者. 现在是时候端出 Beats Audio 的 API 开发工具了,HTC 官方确认即将推出,这个工具允许第三方开发者挖掘 Beats Audio 的潜力,用以增强应用软件的音效体验.

Google Maps API 收费大幅下调,开发者撒花

- - 谷奥——探寻谷歌的奥秘
昨天Google宣布下调Google Maps API的价格. 作为一位开发者,如果你的地图应用每天调用超过25000次Google Maps API,并连续三个月都是如此,那么要继续使用的话就必须付费. 以前费用是每1000次4美元,昨天开始大幅下调到每1000次0.5美元──是以前的1/8,打了一折多.

Filer.js:简化HTML5文件系统API开发的开源JS库

- - HTML5研究小组
在 W3C 的工作草案中,有一个雄心勃勃的底层 Web 标准开发计划即 HTML5 文件系统(Firesystem)API 规范. 所谓 Filesystm API 是一个提供在用户自定义的沙盒文件系统中读取与写入文件及目录的接口. 不过正如文件系统一样,该 API 代码较长而且复杂.   为此 Google Chorme 团队的工程师 Eric Bidelman 写了一个基于它的 JavaScript 包装库,Bidlelman 将其命名为 filer.js 并放在了 GitHub 上.

福特汽车向 iOS/Android 开发者开放 Applink API

- - ITeye资讯频道
据国外媒体报道, 福特今天在CES大展上向iOS和Android开发者开放Applink API,未来开发者可以为汽车开发应用. 互联汽车的理念越来越深入人心,但为汽车开发应用一般都是小范围不公开测试,并且受到汽车厂商的诸多限制. 现在福特成为首家向开发者开放的汽车厂商,今天它在CES大展上宣布了向iOS和Android开发者开放的消息.

[图]Feedly已向所有开发人员开放其云API

- - cnBeta全文版
作为Google Reader的有力继任者之一,Feedly又向前迈出了一大步. 今天,Feedly已经面向所有开发人员,公开了其cloud API. Feedly在一篇博客文章中解释到,该API的开发耗时6月,目的是打造一个"足够简单,让单个开发人员能在一周不到的时间里开发出一个大应用"的环境. 除了包括用户feeds背后的数据,该Feedly API还包括了一个"强大的"个性化图表和应用的跨全平台性.