简体   繁体   中英

Restrict number of API calls per second with Thread.Sleep?

As always, im quite the noob, as im sure you will see from both my code and question. For practice im currently writing an Xamarin.Android app for a game called Eve Online. People there mine resources from planets to make cash. These mines have to be reset at different intervals, and the real pros can have up to 30 characters doing it. Each character can have 5 planets, usually there are at least 2 mines (extractors) on each. So there could be 300 timers going on.

In my app you save your characters in an sqlite db, and every hour a intentservice runs through the API and checks your times and if their expired or not. This is how i do that:

public async Task PullPlanets(long KeyID, long CharacterID, string VCode, string CharName)
    {
        XmlReader lesern = XmlReader.Create("https://api.eveonline.com/char/PlanetaryColonies.xml.aspx?keyID=" + KeyID + "&vCode=" + VCode + "&characterID=" + CharacterID);

        while (lesern.Read())
        {
            long planet = 0;
            string planetName;
            planet = Convert.ToInt64(lesern.GetAttribute("planetID"));
            planetName = lesern.GetAttribute("planetName");

            if ((planet != 0) && (planetName != null))
            {
                planets.Add(planet);
                planetNames.Add(planetName);
                await GetExpirationTimes(CharName, planet, planetName, KeyID, CharacterID, VCode);

            }
        }

        lesern.Close ();
    }



    public async Task GetExpirationTimes(string CharName, long planetID, string planetName, long KeyID, long CharacterID, string VCode)
    {
        string planet = planetID.ToString();
        XmlReader lesern = XmlReader.Create("https://api.eveonline.com/char/PlanetaryPins.xml.aspx?keyID=" + KeyID + "&vCode=" + VCode + "&characterID=" + CharacterID + "&planetID=" + planet);


        while (lesern.Read())
        {
            string expTime;
            expTime = lesern.GetAttribute("expiryTime");

            if ((expTime != null) && (expTime != "0001-01-01 00:00:00"))
            {

                    allInfo.Add (new AllInfo (CharName, planetName, Convert.ToDateTime (expTime)));

            }
        }

        lesern.Close ();

        SendOrderedBroadcast (stocksIntent, null);
    }

}

After this, it sends the times back to my Activity, where they get added to an extractor. It seems to work pretty fine, although ive only been able to test with 2 characters with a total of 14 extractors so far. An alarmmanger in activity calls the service every hour, and it sends a notification. When user opens the activity, it pulls the list from service, sorts it, and displays it. I would welcome input on if this is the way to do it.

I do see a problem in the horizon, though. The Eve API blocks if an app surpases 30 API-calls per second. Im pretty sure someone with 30 characters would do that. So, im wondering if i should add something to delay each call if a certain number is passed? This is how i call the first XML call.

var table = db.Table<CharsList> ();

    foreach (var e in table) {

        long KeyIDOut = Convert.ToInt64(e.KeyID);
        long CharIDOut = Convert.ToInt64(e.CharacterID);
        string VCodeOut = e.VCode.ToString();
        string navnOut = e.Name.ToString();


            PullPlanets(KeyIDOut, CharIDOut, VCodeOut, navnOut);

        }

        CheckTimes ();

    }

Is it viable to add a

 if (table.Count > 10) {

 foreach (var e in table) {
 //start the first characters call
 Thread.Sleep(100)

}

The service is intentservice and not on UI thread. I guess this would bring the calls under 30 a sec, but i have never used Thread.Sleep and fear what else could happen in my code. Are there other things that could help me not blow the limit? Can this code handle 300 extractors?

I believe you are generally right in your approach. I had to do a similar thing for a reddit client I was writing, except their limits is once a second or so.

The only problem I see with your setup is that assume that Thread.Sleep does sleep for the amount of time you give it. Spurious wakeups are possible in some cases, so what I would suggest is that you give it a smaller value, save the last time you accessed the service and then put a loop around the sleep call that terminates once enough time has passed.

Finally if you are going to be firing up a lot of intent services for a relatively short amount of work, you might want to have a normal service with a thread to handle the work - that way it will only have to be created once but it is still of the UI thread.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM