Verifica come la tua app gestisce i limiti di frequenza dell'API di GitHub

Tip

Non conosci ancora il throttling? Scopri che cos'è il throttling e come gestirlo.

A colpo d'occhio
Obiettivo: Testare il modo in cui l'app gestisce i limiti di frequenza dell'API REST di GitHub
Tempo: 15 minuti
Plugin:RateLimitingPlugin, GenericRandomErrorPlugin, RetryAfterPlugin
Prerequisiti:Configurare il proxy di sviluppo

L'app chiama l'API GitHub. Funziona sulla tua macchina, poi un job di CI, una grande organizzazione o una giornata particolarmente intensa lo fanno superare il limite di richieste, e comincia a fallire. Per testarla con l'API reale, dovresti esaurire il limite di richieste e poi attendere fino a un'ora prima di poter riprovare. Dev Proxy simula localmente i limiti di frequenza di GitHub, con un limite e un intervallo di tempo scelti da te.

Scopri cosa restituisce GitHub

GitHub prevede 2 tipi di limiti di frequenza per l'API REST.

I limiti di frequenza primari limitano il numero di richieste eseguite all'ora. Ad esempio, 60 per le richieste non autenticate e 5.000 per le richieste con un token di accesso personale. Ogni risposta include intestazioni che mostrano dove ci si trova:

Header Meaning
x-ratelimit-limit Numero massimo di richieste all'ora
x-ratelimit-remaining Numero di richieste lasciate nella finestra corrente
x-ratelimit-reset Momento in cui la finestra si reimposta, espresso in secondi epoch UTC

Quando si supera il limite primario, GitHub restituisce 403 o 429 con x-ratelimit-remaining impostato su 0. Non riprovare fino all'ora in x-ratelimit-reset.

I limiti secondari proteggono GitHub da picchi di attività, come un numero eccessivo di richieste simultanee o la creazione di troppi contenuti in troppo poco tempo. Quando se ne supera uno, GitHub restituisce 403 o 429 con un messaggio relativo a un limite di frequenza secondario. Se la risposta ha un'intestazione retry-after, attendere quel numero di secondi. In caso contrario, attendere almeno un minuto e aumentare il tempo di attesa se la richiesta continua a non andare a buon fine.

GitHub può vietare le integrazioni che continuano a inviare richieste mentre sono soggette a limitazione di frequenza. Per altre informazioni, vedere Limiti di frequenza per l'API REST nella documentazione di GitHub.

Simula il limite di richieste primario

Usare RateLimitingPlugin per contare le richieste e restituire le intestazioni del limite di frequenza di GitHub. Per eseguire il test senza attendere un'ora, usare un limite ridotto e una finestra breve.

File: devproxyrc.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
  "plugins": [
    {
      "name": "RetryAfterPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
    },
    {
      "name": "RateLimitingPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
      "configSection": "githubRateLimit"
    }
  ],
  "urlsToWatch": [
    "https://api.github.com/*"
  ],
  "githubRateLimit": {
    "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/ratelimitingplugin.schema.json",
    "headerLimit": "x-ratelimit-limit",
    "headerRemaining": "x-ratelimit-remaining",
    "headerReset": "x-ratelimit-reset",
    "resetFormat": "UtcEpochSeconds",
    "costPerRequest": 1,
    "rateLimit": 5,
    "resetTimeWindowSeconds": 60,
    "warningThresholdPercent": 0,
    "whenLimitExceeded": "Custom",
    "customResponseFile": "github-rate-limit-exceeded.json"
  }
}

Caution

Aggiungi RetryAfterPlugin prima di RateLimitingPlugin nel file di configurazione. Se lo si aggiunge dopo, RateLimitingPlugin gestisce la richiesta prima che RetryAfterPlugin possa controllarla.

Nel file di risposta personalizzato definire la risposta che GitHub restituisce quando si supera il limite di frequenza primaria.

File: github-rate-limit-exceeded.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/ratelimitingplugin.customresponsefile.schema.json",
  "statusCode": 429,
  "headers": [
    {
      "name": "content-type",
      "value": "application/json; charset=utf-8"
    }
  ],
  "body": {
    "message": "API rate limit exceeded for user ID 1.",
    "documentation_url": "https://docs.github.com/rest/overview/rate-limits-for-the-rest-api"
  }
}

Avviare Dev Proxy ed eseguire l'app.

devproxy --config-file devproxyrc.json

Dev Proxy inoltra le prime 5 richieste ogni minuto a GitHub e imposta le intestazioni x-ratelimit-* sulle risposte. A partire dalla sesta richiesta, Dev Proxy restituisce la risposta di limitazione della frequenza con x-ratelimit-remaining impostata su 0 e x-ratelimit-reset impostata alla fine della finestra. Se l'app chiama nuovamente l'API prima della reimpostazione della finestra, il tag RetryAfterPlugin lo segnala e limita la richiesta.

Verifica che la tua app:

  • Legge x-ratelimit-remaining e rallenta prima di raggiungere 0.
  • Arresta la chiamata all'API dopo una risposta di limitazione della frequenza e attende fino a x-ratelimit-reset.
  • Indica all'utente cosa sta accadendo, ad esempio "limite di richieste di GitHub raggiunto, ritentando alle 14:05", invece di fallire silenziosamente.

Note

GitHub restituisce 403 o 429 quando si supera un limite di frequenza. Per verificare che anche l'app gestisca 403, modificare statusCode in 403. RetryAfterPlugin tiene traccia solo delle risposte 429, quindi non segnala nuovi tentativi precoci dopo un 403.

Tip

Il proxy di sviluppo inoltra le richieste a GitHub fino al raggiungimento del limite simulato. Queste richieste vengono conteggiate anche rispetto al limite di frequenza reale di GitHub.

Simula i limiti di frequenza secondaria

I limiti di frequenza secondari si verificano a raffiche e includono un'intestazione retry-after. Usare GenericRandomErrorPlugin per restituirli in modo casuale.

File: devproxyrc.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
  "plugins": [
    {
      "name": "RetryAfterPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
    },
    {
      "name": "GenericRandomErrorPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
      "configSection": "githubSecondaryRateLimit"
    }
  ],
  "urlsToWatch": [
    "https://api.github.com/*"
  ],
  "githubSecondaryRateLimit": {
    "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.schema.json",
    "errorsFile": "github-secondary-rate-limit.json",
    "rate": 50,
    "retryAfterInSeconds": 60
  }
}

File: github-secondary-rate-limit.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.errorsfile.schema.json",
  "errors": [
    {
      "request": {
        "url": "https://api.github.com/*"
      },
      "responses": [
        {
          "statusCode": 429,
          "headers": [
            {
              "name": "content-type",
              "value": "application/json; charset=utf-8"
            },
            {
              "name": "retry-after",
              "value": "@dynamic"
            }
          ],
          "body": {
            "message": "You have exceeded a secondary rate limit. Please wait a few minutes before you try again.",
            "documentation_url": "https://docs.github.com/rest/overview/rate-limits-for-the-rest-api#about-secondary-rate-limits"
          }
        }
      ]
    }
  ]
}

Avviare Dev Proxy ed eseguire l'app. Verificare che l'app attenda il numero di secondi specificato nell'intestazione retry-after prima di chiamare nuovamente l'API. Se non lo fa, RetryAfterPlugin lo segnala.

Se utilizzi Octokit con il plugin di throttling, verifica che i gestori onRateLimit e onSecondaryRateLimit vengano eseguiti e restituiscano il risultato previsto.

Passaggio successivo

Ulteriori informazioni su RateLimitingPlugin.

Vedere anche