You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Angular中避免429请求过多错误?Laravel后端该如何处理?

解决Laravel后端429 Too Many Requests错误:Angular前端与后端优化方案

问题描述

我尝试在每个API调用中加入delay(50),但还是收到429请求过多错误。后端用的是Laravel框架,想知道在Angular里避免这个错误的最佳方式是什么?还是应该由Laravel后端来处理?

我的Angular代码

export class PdoProgramComponent implements OnInit {
  ngOnInit() {
    this.loadPdoPrograms();
  }
  // Load all PDO programs from DB
  loadPdoPrograms() {
    this.isLoading = true;
    this.pdoService.getAssigendPdos().pipe(
      take(1),
      map(res => this.sortPdosByName(res.results.pdos)),
      switchMap(pdos => this.validatePdoPrograms(pdos)),
      catchError(err => {
        this.showAlertSavingFailed();
        return of([]);
      })
    ).subscribe({
      next: (pdos: Pdo[]) => this.pdos = pdos,
      complete: () => this.isLoading = false
    });
  }
  validatePdoPrograms(pdos: Pdo[]): Observable<Pdo[]> {
    return from(pdos).pipe(
      concatMap(pdo => of(pdo).pipe(
        delay(50), // Delay each call by 50 milliseconds
        switchMap(() => this.pdoService.getSpecificPdoAdvocaiesPrograms(pdo.id).pipe(
          take(1),
          map(res => {
            const pdo_advocacies = res.results.pdo?.pdo_advocacies;
            pdo.editRequiredCount = 0;

            pdo_advocacies.forEach(advocacy => {
              if (advocacy.advocacy !== 'Unrestricted') {
                pdo.editRequiredCount += advocacy.pdo_programs.filter(
                  program => program.admin_advocacies.length === 0 ||
                    !program.admin_beneficiary_category ||
                    !program.program_category
                ).length;
              }
            });

            return pdo;
          })
        ))
      )),
      toArray()
    );
  }
}

一、Angular前端优化方案

1. 动态退避重试策略

固定50ms延迟可能无法匹配后端限流阈值,改用指数退避+重试逻辑,同时根据后端返回的Retry-After头动态调整等待时间:

validatePdoPrograms(pdos: Pdo[]): Observable<Pdo[]> {
  return from(pdos).pipe(
    concatMap((pdo, index) => of(pdo).pipe(
      // 基础延迟随请求次数递增
      delay(50 * Math.pow(2, index)),
      switchMap(() => this.pdoService.getSpecificPdoAdvocaiesPrograms(pdo.id).pipe(
        take(1),
        map(res => {
          // 原有的数据处理逻辑
          const pdo_advocacies = res.results.pdo?.pdo_advocacies;
          pdo.editRequiredCount = 0;

          pdo_advocacies.forEach(advocacy => {
            if (advocacy.advocacy !== 'Unrestricted') {
              pdo.editRequiredCount += advocacy.pdo_programs.filter(
                program => program.admin_advocacies.length === 0 ||
                  !program.admin_beneficiary_category ||
                  !program.program_category
              ).length;
            }
          });

          return pdo;
        }),
        // 捕获429错误后自动重试
        retryWhen(errors => errors.pipe(
          mergeMap(error => {
            if (error.status === 429) {
              const retryAfter = error.headers.get('Retry-After');
              // 优先用后端指定的重试时间,否则用指数退避
              const delayMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : 1000 * Math.pow(2, index);
              return timer(delayMs);
            }
            return throwError(() => error);
          })
        ))
      ))
    )),
    toArray()
  );
}

2. 合并请求减少调用次数

如果后端支持,将多个单PDO查询请求合并为批量查询接口,把N次请求压缩为1次,从根源上避免限流:

// 在PdoService中新增批量请求方法
getBatchPdoAdvocaiesPrograms(pdoIds: number[]): Observable<any> {
  return this.http.post('/api/pdos/batch-advocacies', { pdo_ids: pdoIds });
}

// 改造validatePdoPrograms方法
validatePdoPrograms(pdos: Pdo[]): Observable<Pdo[]> {
  const pdoIds = pdos.map(p => p.id);
  return this.pdoService.getBatchPdoAdvocaiesPrograms(pdoIds).pipe(
    map(res => {
      // 批量结果匹配到对应的PDO对象
      res.results.pdos.forEach(batchPdo => {
        const targetPdo = pdos.find(p => p.id === batchPdo.id);
        if (targetPdo) {
          const pdo_advocacies = batchPdo.pdo_advocacies;
          targetPdo.editRequiredCount = 0;

          pdo_advocacies.forEach(advocacy => {
            if (advocacy.advocacy !== 'Unrestricted') {
              targetPdo.editRequiredCount += advocacy.pdo_programs.filter(
                program => program.admin_advocacies.length === 0 ||
                  !program.admin_beneficiary_category ||
                  !program.program_category
              ).length;
            }
          });
        }
      });
      return pdos;
    })
  );
}

3. 前端缓存重复请求

对已查询过的PDO数据做缓存,避免重复发起相同请求:

// 在PdoService中添加缓存逻辑
private pdoAdvocaciesCache = new Map<number, Observable<any>>();

getSpecificPdoAdvocaiesPrograms(pdoId: number): Observable<any> {
  if (!this.pdoAdvocaciesCache.has(pdoId)) {
    const request = this.http.get(`/api/pdos/${pdoId}/advocacies`).pipe(
      take(1),
      shareReplay(1) // 缓存请求结果
    );
    this.pdoAdvocaciesCache.set(pdoId, request);
  }
  return this.pdoAdvocaciesCache.get(pdoId)!;
}

二、Laravel后端优化方案

1. 调整限流规则

修改Laravel默认的throttle中间件配置,根据业务需求放宽限流阈值:

// 在routes/api.php中调整单接口限流
Route::get('/pdos/{id}/advocacies', [PdoController::class, 'getAdvocacies'])
    ->middleware('throttle:100,1'); // 允许每分钟100次请求

// 针对登录用户单独配置更宽松的限流
Route::middleware(['auth:sanctum', 'throttle:200,1'])->group(function () {
    Route::get('/pdos/{id}/advocacies', [PdoController::class, 'getAdvocacies']);
});

2. 新增批量查询接口

提供批量查询接口,减少前端请求次数:

// 在PdoController中新增批量接口
public function getBatchAdvocacies(Request $request) {
    $request->validate([
        'pdo_ids' => 'required|array|exists:pdos,id'
    ]);

    // 预加载关联数据,提升查询效率
    $pdos = Pdo::whereIn('id', $request->pdo_ids)
        ->with('pdo_advocacies.pdo_programs')
        ->get();

    return response()->json([
        'results' => [
            'pdos' => $pdos
        ]
    ]);
}

3. 后端缓存查询结果

用Redis或Laravel缓存机制缓存PDO查询结果,减少数据库压力同时加快响应:

public function getAdvocacies($id) {
    // 缓存1小时
    $pdo = Cache::remember("pdo_advocacies_{$id}", 3600, function () use ($id) {
        return Pdo::findOrFail($id)->load('pdo_advocacies.pdo_programs');
    });

    return response()->json([
        'results' => [
            'pdo' => $pdo
        ]
    ]);
}

总结

最优方案是前后端配合优化:前端通过批量请求、缓存、退避重试减少触发限流的概率;后端通过调整规则、提供批量接口、缓存数据从根源上缓解限流问题。如果只能优先修改一端,建议先改后端提供批量接口,这比前端单纯加延迟更高效可靠。

内容的提问来源于stack exchange,提问作者lance2k

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 16:12:05