我正在开发一个Angular应用程序。我使用其中一个社交网络提供的API,它仅限每秒5次API调用。
最直接的解决方案是编写自定义逻辑,计算请求并将其排队到限制。因此,如果我在一秒钟内向API发送第6个请求,则会在第一个请求发送后的第二个请求发送。
但是如果可以使用RxJs,我想找到一些优雅的解决方案。
例如,我可以为 Observable 设置 debounseTime ,如下例所示。但实际上我得到的是,我不能在行中发出一些间隔小于200毫秒的请求。
this.searchControl.valueChanges
.debounceTime(200) // 200ms ~ 5 requests per second
.switchMap(search => this.api.searchPeople(search))
RxJs是否有任何技术可以限制每个间隔的一些发射并在发送请求过于频繁的情况下对它们进行排队?
答案 0 :(得分:1)
您可以跟踪最近调用api的次数。因此,如果你每秒可以进行5次呼叫,那么意味着你有5个令牌,如果一个令牌被消耗,那么它将在一秒钟之后更新。我已经制作了以下运算符,可以满足您的需求:
Observable.prototype.rateLimit = function (count: number, slidingWindowTime: number, scheduler = async) {
let tokens = count;
const tokenChanged = new BehaviorSubject(tokens);
const consumeToken = () => tokenChanged.next(--tokens);
const renewToken = () => tokenChanged.next(++tokens);
const availableTokens = tokenChanged.filter(() => tokens > 0);
return this.mergeMap(value =>
availableTokens
.take(1)
.map(() => {
consumeToken();
Observable.timer(slidingWindowTime, scheduler).subscribe(renewToken);
return value;
}));
}
declare module 'rxjs/Observable' {
interface Observable < T > {
rateLimit(count: number, slidingWindowTime: number, scheduler ? : Scheduler): Observable < T >
}
}
&#13;
答案 1 :(得分:1)
如果您对更多Angular方法感兴趣,下面是Angular 6和RxJS 6的拦截器(基于原始答案):
@Injectable()
export class RequestRateLimitInterceptor implements HttpInterceptor {
private readonly timeFrame = 1000 /* 1 second time frame */;
private availableThreads = 10 /* 10 requests per the time frame */;
private rateLimit$ = new BehaviorSubject(this.availableThreads);
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.rateLimit$.pipe(
filter(() => this.availableThreads > 0),
take(1),
tap(() => this.removeThreads()),
tap(() => timer(this.timeFrame).subscribe(() => this.addThreads())),
mergeMap(() => next.handle(req))
);
}
private addThreads(): void {
this.changeThreads(1);
}
private removeThreads(): void {
this.changeThreads(-1);
}
private changeThreads(value: number): void {
this.rateLimit$.next(this.availableThreads += value);
}
}