HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Throttling REST requests in C#

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
restthrottlingrequests

Problem

I'm rather new to C#, and found relevant subclassing examples surprisingly hard to come by. I've whipped up this class that appears to work, but am pretty sure this is not optimal:

private class ThrottledRestClient
{
    int pause;  // mininum ms between requests
    double rpm;  // max requests per minute
    long last_request_time;
    RestClient client;
    public ThrottledRestClient(string url, HttpBasicAuthenticator auth, double rpm = 60)
    {
        this.client = new RestClient(url);
        this.client.Authenticator = auth;
        this.rpm = rpm;
        this.pause = (int)((60 / rpm) * 1000);
    }

    public IRestResponse Execute(RestRequest request)
    {
        long now = DateTime.Now.Ticks;
        int wait = pause - (int)((now - this.last_request_time) / TimeSpan.TicksPerMillisecond);
        if (wait > 0)
        {
            //Console.WriteLine("Waiting ms: " + wait);
            Thread.Sleep(wait);
        }
        this.last_request_time = DateTime.Now.Ticks;

        return this.client.Execute(request);
    }
}


How could this be better? Is there an existing solution I haven't found?

Solution

I'm rather new to C#, and found relevant subclassing examples
surprisingly hard to come by

You just needed to try a bit harder! You are doing pretty much what you were supposed to do logic wise. What you are trying to do here obviously (maybe), is a specialization of a RestClient, so the RestClient class is the best candidate to inherit from. From there you may override the Execute method.

Don't be afraid to use verbose variables. The rpm name could be firstly reminded as rotations per minute which doesn't really apply here. Also try to name your instances fields in camelCase, you may or may not prefix them with _.

I also didn't get why your pause is (60 * 1000) / rpm = 1000 with the default rpm, but one thing I know is that you don't need to store it in a field because it may be calculated. I would also recommend using Environment.TickCount instead of DateTime.Now.Ticks, which is more performant.

With those modifications the code would become the following:

public class ThrottledRestClient : RestClient
{
    private readonly int _requestsPerMinute;
    private int _lastRequestTime;

    public ThrottledRestClient(int requestsPerMinute)
    {
        _requestsPerMinute = requestsPerMinute;
    }
    public override IRestResponse Execute(IRestRequest request)
    {
        int elapsedTime = Environment.TickCount - _lastRequestTime;
        int pause = (60/_requestsPerMinute)*1000;
        int wait = pause - elapsedTime;
        if (wait > 0)
        {
            Thread.Sleep(wait);
        }
        var response = base.Execute(request);
        _lastRequestTime = Environment.TickCount;
        return response;
    }
}


EDIT: To answer you comment. You can use this client like you would use the previous one eg:

var client = new ThrottledRestClient(60);
client.Authenticator = new HttpBasicAuthenticator("user", "password");
//so on so forth;
client.BaseUrl = "http://google.pt";
client.Request(new RestRequest());

Code Snippets

public class ThrottledRestClient : RestClient
{
    private readonly int _requestsPerMinute;
    private int _lastRequestTime;

    public ThrottledRestClient(int requestsPerMinute)
    {
        _requestsPerMinute = requestsPerMinute;
    }
    public override IRestResponse Execute(IRestRequest request)
    {
        int elapsedTime = Environment.TickCount - _lastRequestTime;
        int pause = (60/_requestsPerMinute)*1000;
        int wait = pause - elapsedTime;
        if (wait > 0)
        {
            Thread.Sleep(wait);
        }
        var response = base.Execute(request);
        _lastRequestTime = Environment.TickCount;
        return response;
    }
}
var client = new ThrottledRestClient(60);
client.Authenticator = new HttpBasicAuthenticator("user", "password");
//so on so forth;
client.BaseUrl = "http://google.pt";
client.Request(new RestRequest());

Context

StackExchange Code Review Q#78432, answer score: 4

Revisions (0)

No revisions yet.