-1
votes

Comment faire une boucle Itération 60 fois par seconde?

Je interrogee une API qui a une limite au nombre de requêtes que vous pouvez faire une seconde. Par exemple, vous serez peut-être autorisé à faire 20 interrogations une seconde. Si vous avez surveillé le serveur, vous obtenez une erreur 429 qui vous empêche d'accéder à l'API.

Le problème vient avec une boucle de Foreach dans mon code où chaque itération du code nécessite une requête de l'API. Existe-t-il un moyen de coder dans la limite de sorte que la foreach n'atteignait que un certain nombre de fois dans une limite de temps définie pour que je n'atteigne pas la limite de requête de l'API? Ou en d'autres termes, puis-je faire ma boucle foreach itérer à une vitesse de 20 boucles une seconde ou tout autre numéro une seconde? P>

La boucle de Foreach est ci-dessous si vous voulez le regarder mais je ne le regardais pas Croyez que vous en aurez besoin pour répondre à la question. P>

foreach(var item in matchlistd)
{
    var response2 = client.GetAsync($@"https://na1.api.riotgames.com/lol/summoner/v4/summoners/by-name/{item.summonerName}apikeyiswhatgoesintherestofthispartoftheapi).Result;
    if (response2.IsSuccessStatusCode)
    {
        var content2 = response2.Content.ReadAsStringAsync().Result;
        summonerName player = JsonConvert.DeserializeObject<summonerName>(content2);
        accountinfo.Add(player);
    }
}


6 commentaires

Utilisez une minuterie à la place. Ce n'est pas un travail de la boucle


Vous avez déjà fui votre clé API, assurez-vous de le régénérer.


@Pluclv J'étais ignoré de la classe de la minuterie. Va regarder dans ceci.


Pour calculer cela dans une boucle, vous devez connaître avec précision la durée de la durée de chaque demande, ce qui signifie que vous devez être en mesure de garantir que la latence ne changerait jamais entre vous et le serveur. Toute différence mineure dans une seule condition signifierait que votre boucle a échoué. Cela signifie qu'une boucle n'est pas l'approche appropriée ici.


@nlawalker oui je me suis rendu compte que. Je suis allé de l'avant et a fait l'édition et la régénéra de toute façon juste pour être en sécurité.


.result est potentiellement dangereux peut pouvoir conduire à une liaison morte.


3 Réponses :


0
votes

Utilisez une minuterie comme ci-dessous. Vous pouvez également passer une variable pour l'intervalle de minuterie.

private Timer _functionTimer; 

public void InitMatchList()
{
    _functionTimer_Tick = new Timer();
    _functionTimer_Tick.Tick += new EventHandler(_functionTimer_Tick);
    _functionTimer_Tick.Interval = 50; // in miliseconds
    _functionTimer_Tick.Start();
}

private void _functionTimer_Tick(object sender, EventArgs e)
{
    var response2 = client.GetAsync($@"https://na1.api.riotgames.com/lol/summoner/v4/summoners/by-name/{item.summonerName}apikeyiswhatgoesintherestofthispartoftheapi).Result;
    if (response2.IsSuccessStatusCode)
    {
        var content2 = response2.Content.ReadAsStringAsync().Result;
        summonerName player = JsonConvert.DeserializeObject<summonerName>(content2);
        accountinfo.Add(player);
    }
}


0 commentaires

0
votes

tandis que une minuterie peut fonctionner em> C'est une manière ancienne de faire de la limitation de tarification. Cela peut ne pas fonctionner si le serveur ne veut pas plus de 20 connexions conneccurrent em> par seconde. Signification Si le résultat prend plus de temps que la prochaine demande chronométrée em> Vous pouvez toujours obtenir une 429. Une solution alternative pourrait ressembler à:

private static readonly httpClient = new HttpClient();
public async Task<IEnumerable<string>> GetAPIResults(IEnuemrable<MatchList> matchLists,
  int maximumRequestsPerSecond)
{
    var requests = matchLists
      .Select(ml => new RequestStatus { MatchList = ml })
      .ToList();

    foreach (var request in requests)
    {
      var activeRequests = RequestStatus
        .Where(rs => 
          (rs.RequestedOn.HasValue && rs.RequestedOn > DateTime.Now.AddSeconds(-1))
          || (rs.Task.HasValue && rs.Task.TaskStatus != TaskStatus.Running))
        .ToList();

      //wait for either a request to complete
      //or for a request not active within the last second to expire
      while (activeRequests > maximumRequestsPerSecond)
      {
        var lastActive = activeRequests.OrderBy(RequestedOn.Value).First();
        var waitFor = DateTime.Now - lastActive.RequestedOn.Value;

        // or maybe this to be safe
        // var waitFor = (DateTime.Now - lastActive.RequestedOn.Value)
        //   .Add(TimeSpan.FromMilliseconds(100));

        await Task.Delay(waitFor);

        activeRequests = RequestStatus
          .Where(rs => 
            (rs.RequestedOn.HasValue && rs.RequestedOn > DateTime.Now.AddSeconds(-1))
            || (rs.Task.HasValue && rs.Task.TaskStatus != TaskStatus.Running))
          .ToList();
    }

      request.RequestTask = httpClient.GetStringAsync(myUrl);       
    }

    await Task.WhenAll(requests.Select(r => r.RequestTask.Value));

    // not sure about .Result here...
    return requests.Select(r => r.RequestTask.Value.Result).ToList();

    // probably safer:
    return requests.Select(r => await r.RequestTask).ToList();
}

public class RequestStatus
{
  public MatchList MatchList { get; set; }
  public DateTime? RequestedOn { get; set }
  public Task<string>? RequestTask { get; set; }
}


1 commentaires

Je suis d'accord C'est une bien meilleure solution Erik qui ne dépassera pas les connexions HTTP!



1
votes

Vous devez utiliser le cadre réactif de Microsoft (AKA RX) - Nuget System.Reactive CODE> et Ajouter à l'aide de system.reactive.linq; code> - alors vous pouvez faire des trucs assez cool .

Tout d'abord, nous devons corriger votre code afin que vous n'ayez pas à vous fier sur les appels .result code>. p>

supposons simplement que votre code est en cours d'exécution. Dans une méthode appelée principale code> - vous pouvez modifier votre code pour fonctionner comme ceci: p> xxx pré>

notez le async code> et Les deux attendre code> mots-clés. P>

Réécrivez maintenant votre boucle pour être un RX observable. Un observable est comme un énumérable, mais au lieu de produire toutes les valeurs immédiatement, il produit des valeurs une à la fois. P>

    from items in matchlistd.ToObservable().Buffer(20).Zip(Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1.0)), (x, t) => x)
    from item in items


0 commentaires