接口调用限制次数

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了接口调用限制次数相关的知识,希望对你有一定的参考价值。

参考技术A 频率限制的应用还会回传三个响应头:

X-RateLimit-Limit告诉我们在指定时间内允许的最大请求次数,
X-RateLimit-Remaining指的是在指定时间段内剩下的请求次数,
Retry-After指的是距离下次重试请求需要等待的时间(s)

访问路由/api/users时你会看见响应头里有如下的信息:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58

如果请求超频,响应头里会返回Retry-After:

Retry-After: 58
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0

上面的信息表示58秒后页面或者API的访问才能恢复正常。

定义频率和重试等待时间
频率默认是60次可以通过throttle中间件的第一个参数来指定你想要的频率,重试等待时间默认是一分钟可以通过throttle中间件的第二个参数来指定你想要的分钟数。

参考: https://juejin.im/post/5b29ccb5e51d4558a846bd93

参考: https://my.oschina.net/aibati2008/blog/692910

如何限制for循环中的网络调用次数?

【中文标题】如何限制for循环中的网络调用次数?【英文标题】:How to limit the number of network calls in a for loop? 【发布时间】:2020-01-18 15:38:16 【问题描述】:

我在服务器上为每个用户提供了一些对象,我必须全部获取它们。首先,我从服务器获取对象计数,以计算我必须分批进行的网络调用次数 30。假设有 2000 个对象,那么我必须进行的调用次数为 2000/30 = 67。我为第一次调用设置偏移量 = 0 和限制 = 29,然后将偏移量和限制都增加 30 并进行第二次调用,依此类推。问题是,如果我在 for 循环中同时发送超过 8 个调用,那么调用开始失败,即使我重试它仍然失败。我正在使用 Alamofire 向服务器发送发布请求。我注意到,如果我并行发送 8 个请求,一切似乎都很好。如何确保并行进行 8 个调用,当所有 8 个调用都完成后,再向服务器发送另外 8 个调用。

这是一段代码:

 let count = Double(totalTransactionsCount)
  //The maximum amount of transactions to fetch in one call.
  //let maxLimit = 30
  let calls = Int(ceil(count/Double(maxLimit)))

 for call in 1...calls 
        print("Calling call \(call) with offset:\(offset) to limit:\(limit)")
        callFetchTransactionWith(offset, limit)

        offset += maxLimit
        limit += maxLimit
    


 fileprivate func callFetchTransactionWith(_ offset: Int, _ limit: Int, _ callCountPercentage: Double, _ calls: Int) 
    TransactionsModel.reportTransactions(offset: offset, limit: limit ,success:  (transactions) in
        ACTIVITYCALL += 1
        self.currentSyncingProgress = self.currentSyncingProgress + CGFloat(callCountPercentage)
        Utility.logger(log: "\(self.currentSyncingProgress)", message: "Activity Increment Progress")
        if ACTIVITYCALL == calls 
            TransactionsModel.assignSequenceNumbers()
            self.didCompleteProgressAndSync(duration: 2.0)
            return
         else 
            self.updateProgressBar(value: self.currentSyncingProgress)
        
    , failure:  (response, statusCode) in
        print(response,statusCode)
        self.callFetchTransactionWith(offset, limit, callCountPercentage, calls)
    )



static func reportTransactions(offset:Int,limit:Int,
    success:@escaping ( _ model: [TransactionsModel] ) -> Void,
    failure:@escaping APIClient.FailureHandler) 

    let params:Parameters = [
        "offset":offset,
        "limit":limit
    ]

    let headers:[String:String] = [
        "X-Auth-Token":Singleton.shared.token
    ]

    if !Connectivity.isConnectedToInternet 
        Constants.UIWindow?.showErrorHud(text: AppString.internetUnreachable)
        return
    

    APIClient.shared().requestPost(endpoint: Route.reportTransactions, params: params, headers: headers, success:  (response) in
        guard
            let data = response as? [String:Any],
            let transactions = data["transactions"] as? [[String : Any]]
            else return

        let transactionModels = Mapper<TransactionsModel>().mapArray(JSONArray: transactions)
        TransactionsModel.save(transactions: transactionModels)

        success(transactionModels)
    )  (response, status) in
        print(response,status)
        failure(response,status)
    

【问题讨论】:

【参考方案1】:

信号量

这看起来是一个很好的信号量用例。

致谢:https://unsplash.com/photos/5F04PN6oWeM

让我向您展示如何使用 Playground 执行此操作,以便您可以在本地运行此解决方案,然后将其导入到您的项目中。

游乐场

首先创建一个新的空 Playground 页面和这 3 行,以便导入所需的库并启用并发。

import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

一旦您决定将此解决方案移动到您的项目中,您只需要 import Foundation 行。

目录

现在让我们定义以下 3 个常量。

并发调用 这是您要执行的并发调用数。

let concurrentCalls = 8

信号量 该信号量将允许执行不超过 8 个线程。当第 9 个线程请求访问时,它会处于等待状态,直到正在运行的 8 个线程之一完成。

let semaphore = DispatchSemaphore(value: concurrentCalls)

后台队列 我们将使用此队列异步调度所有调用。

let backgroundQueue = DispatchQueue(label: "Background queue")

fetchData(完成:

此函数模拟您的远程 API 调用。它只是等待 3 秒,然后通过代表结果的“?”字符串调用完成。

func fetchData(completion: @escaping (String) -> Void) 
    DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) 
        completion("?")
    

fetchAll(完成:)

现在我们处于解决方案的核心。

func fetchAll(completion: @escaping ([String]) -> Void) 
    // 1
    let storageQueue = DispatchQueue(label: "Serial queue")

    var results = [String]()
    let totalNumberOrCalls = 20

    for i in 0..<totalNumberOrCalls 
        backgroundQueue.async 
            semaphore.wait()
            fetchData  result in
                storageQueue.async 
                    results.append(result)
                    if i == totalNumberOrCalls - 1 
                        completion(results)
                    
                
                // 2
                semaphore.signal()
            
        
    

它是如何工作的?

我们有一个值设置为 8 的信号量。

每次我们想要执行网络调用时,我们都会询问信号量是否可以开始调用

// 1
semaphore.wait()

如果信号量的值大于 0,则它允许我们的远程调用并减少它的值。

否则,如果信号量保持为 0,则网络调用未执行,而是将其置于 wait 直到之前的调用之一结束。

一旦网络调用结束,我们就会调用

// 2
semaphore.signal

这样,信号量值增加 1,并允许执行另一个等待调用。

测试

现在我们调用调用

fetchAll  results in
    print(results)

fetchData 的并发调用不会超过 8 个,一旦所有调用完成,就会打印结果

["?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]

结论

希望这会有所帮助,如果您想了解更多详细信息,请查看我谈论信号量的这个答案https://***.com/a/51816661/1761687

【讨论】:

if i == totalNumberOrCalls - 1 。给定的结果可能是错误的,因为与其他任务相比,它可能是一个非常短的任务,它将在其他任务完成之前执行(除非信号量的值为 1)。为了防止您必须对已执行的任务进行计数器,当counter == totalNumberOfCalls - 1 时,我们才会到达最后一个

以上是关于接口调用限制次数的主要内容,如果未能解决你的问题,请参考以下文章

java如何实现限制一个接口每天被调用次数为5次?

Java 接口调用速度限制一般是怎么实现的?

API调用次数限制实现

几个免费API接口分享,调用完全不限次数...

新浪股票的api接口可以调用多少次

基于AOP和Redis实现对接口调用情况的接口及IP限流