Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
RxSwift的利率限制_Swift_Reactive Programming_Rx Swift - Fatal编程技术网

RxSwift的利率限制

RxSwift的利率限制,swift,reactive-programming,rx-swift,Swift,Reactive Programming,Rx Swift,我正在寻找一种在HTTP客户机中实现速率限制的智能方法。让我们假设API上的速率限制为每秒5个请求。目前的实施情况与此类似: final class HTTPClient: HTTPClientProtocol { func getUser() -> Observable<User> { return Observable<User>.create { (observer) -> Disposable in .

我正在寻找一种在HTTP客户机中实现速率限制的智能方法。让我们假设API上的速率限制为每秒5个请求。目前的实施情况与此类似:

final class HTTPClient: HTTPClientProtocol {

    func getUser() -> Observable<User> {
        return Observable<User>.create { (observer) -> Disposable in
            ...
        }
    }

    func getProfile() -> Observable<Profile> {
        return Observable<Profile>.create { (observer) -> Disposable in
            ...
        }
    }

    func getMessages() -> Observable<Messages> {
        return Observable<Messages>.create { (observer) -> Disposable in
            ...
        }
    }

    func getFriends() -> Observable<Friends> {
        return Observable<Friends>.create { (observer) -> Disposable in
            ...
        }
    }

}
final类HTTPClient:HTTPClientProtocol{
func getUser()->可观察{
return Observable.create{(observator)->Disposable in
...
}
}
func getProfile()->可观察{
return Observable.create{(observator)->Disposable in
...
}
}
func getMessages()->可观察{
return Observable.create{(observator)->Disposable in
...
}
}
func getFriends()->可观察{
return Observable.create{(observator)->Disposable in
...
}
}
}
现在,理想情况下,我希望在整个应用程序中根据需要使用这些方法,而不必担心速率限制

回到每秒5个请求的例子:前5个请求可以立即执行。但之后的所有请求都必须等待。因此,在1秒的时间内,最多可以执行5个请求。所有其他请求都必须等待


在RxSwift中有什么聪明的方法可以做到这一点吗?

您需要一个自定义的调度程序

final class DelayScheduler: ImmediateSchedulerType {

    init(delay: TimeInterval, queue: DispatchQueue = .main) {
        self.queue = queue
        dispatchDelay = delay
    }

    func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
        let cancel = SingleAssignmentDisposable()
        lastDispatch = max(lastDispatch + dispatchDelay, .now())
        queue.asyncAfter(deadline: lastDispatch) {
            guard cancel.isDisposed == false else { return }
            cancel.setDisposable(action(state))
        }
        return cancel
    }

    var lastDispatch: DispatchTime = .now()
    let queue: DispatchQueue
    let dispatchDelay: TimeInterval
}
final class DelayScheduler:ImmediateSchedulerType{
初始化(延迟:时间间隔,队列:DispatchQueue=.main){
self.queue=队列
调度延迟=延迟
}
func计划(uu状态:StateType,操作:@escaping(StateType)->一次性)->一次性{
让cancel=SingleAssignmentDispossible()
lastDispatch=max(lastDispatch+dispatchDelay.now())
queue.asyncAfter(截止日期:lastDispatch){
guard cancel.isDisposed==false否则{return}
取消。设置一次性(操作(状态))
}
退票取消
}
var lastDispatch:DispatchTime=.now()
let队列:DispatchQueue
让dispatchDelay:时间间隔
}
然后,通过让所有可观察对象订阅此计划程序来实现服务:

final class HTTPClient: HTTPClientProtocol {

    func getUser() -> Observable<User> {
        return Observable<User>.create { (observer) -> Disposable in
            ...
        }.subscribeOn(scheduler)
    }

    func getProfile() -> Observable<Profile> {
        return Observable<Profile>.create { (observer) -> Disposable in
            ...
        }.subscribeOn(scheduler)
    }

    func getMessages() -> Observable<Messages> {
        return Observable<Messages>.create { (observer) -> Disposable in
            ...
        }.subscribeOn(scheduler)
    }

    func getFriends() -> Observable<Friends> {
        return Observable<Friends>.create { (observer) -> Disposable in
            ...
        }.subscribeOn(scheduler)
    }

    let scheduler = DelayScheduler(delay: 0.5)
}
final类HTTPClient:HTTPClientProtocol{
func getUser()->可观察{
return Observable.create{(observator)->Disposable in
...
}.subscribeOn(调度程序)
}
func getProfile()->可观察{
return Observable.create{(observator)->Disposable in
...
}.subscribeOn(调度程序)
}
func getMessages()->可观察{
return Observable.create{(observator)->Disposable in
...
}.subscribeOn(调度程序)
}
func getFriends()->可观察{
return Observable.create{(observator)->Disposable in
...
}.subscribeOn(调度程序)
}
let scheduler=DelayScheduler(延迟:0.5)
}

Daniel T使用自定义调度程序非常出色,我发现它在实践中运行良好。下面是他的代码版本,它实现了真正的滑动窗口速率限制:

final class RateLimitedScheduler: ImmediateSchedulerType {

    let period: TimeInterval
    let queue: DispatchQueue

    var dispatchHistory: [DispatchTime]
    var dhIndex = 0

    init(maxEvents: Int, period: TimeInterval, queue: DispatchQueue = .main) {
        self.period = period
        self.queue = queue
        let periodAgo = DispatchTime.now() - period
        dispatchHistory = Array(repeating: periodAgo, count: maxEvents)
    }

    func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable {
        let cancel = SingleAssignmentDisposable()
        queue.asyncAfter(deadline: nextDeadline()) {
            guard cancel.isDisposed == false else { return }
            cancel.setDisposable(action(state))
        }
        return cancel
    }

    private func nextDeadline() -> DispatchTime {
        let windowStartTime = dispatchHistory[dhIndex]
        let deadline = max(windowStartTime + period, DispatchTime.now())
        dispatchHistory[dhIndex] = deadline
        dhIndex = (dhIndex >= dispatchHistory.count - 1) ? 0 : (dhIndex + 1)
        return deadline
    }

}
最终类RateLimitedScheduler:ImmediateSchedulerType{
let周期:时间间隔
let队列:DispatchQueue
var调度历史:[调度时间]
var-dhIndex=0
init(maxEvents:Int,句点:时间间隔,队列:DispatchQueue=.main){
self.period=period
self.queue=队列
让periodAgo=DispatchTime.now()-period
dispatchHistory=数组(重复:periodAgo,计数:maxEvents)
}
func计划(uu状态:StateType,操作:@escaping(StateType)->一次性)->一次性{
让cancel=SingleAssignmentDispossible()
queue.asyncAfter(截止日期:nextDeadline()){
guard cancel.isDisposed==false否则{return}
取消。设置一次性(操作(状态))
}
退票取消
}
private func nextDeadline()->DispatchTime{
让WindowsStartTime=dispatchHistory[dhIndex]
让deadline=max(WindowsStartTime+期间,DispatchTime.now())
dispatchHistory[dhIndex]=截止日期
dhIndex=(dhIndex>=dispatchHistory.count-1)?0:(dhIndex+1)
返回截止日期
}
}

请注意,完美的准确性要求跟踪前N个条目的调度时间,因此对于每个周期数百或数千次操作的速率来说,内存非常昂贵。考虑对这些情况使用“令牌桶”——它不太精确,但只需要恒定状态(参见)。< / P>两个链接问题的可能副本不显示如何处理“每秒2请求”。链接的答案部分正确。您将需要一个服务的专用调度程序,但它必须是一个自定义调度程序。。。我还没有做自定义调度程序,我必须考虑一下这个。谢谢你的代码。不幸的是,这不是我想要的。无论是否有必要,您的解决方案都会给n+1请求一个延迟。在真实场景中,速率限制为每秒5个请求。因此,您可以立即执行其中的5个,但对于第6个请求,您需要稍等。