我从一个安静的api请求一个资源而不是http。资源需要时间才能达到“完整”状态,响应的http状态代码反映了资源的完整性。
调用客户端是等待消耗不想无限期等待的资源的用户。因此,api被轮询到获得“完整”资源的最大尝试次数。如果api返回指示资源已完成的状态,则资源将返回堆栈以供使用。如果api没有返回指示资源在最大尝试次数内完成的状态,那么客户端应该停止轮询并返回某种失败状态。
为了实现这一点,我正在使用Thread.Sleep(500)
来阻止等待结果的线程。它让我做噩梦,想到这是多么低效,所以我希望改进这项技术。
我已经调查了Rx
并徒劳地试图将Observable.Interval()
弯曲到我的意愿,但我在调试线程上试图观察时遇到了麻烦。我理想地想知道我的代码应该是什么样的!目前在:
Observable.Interval(TimeSpan.FromMilliseconds(500))
.ObserveOn(SynchronizationContext.Current)
.Take(10)
.Subscribe(i =>
{
// check if resource is complete
});
答案 0 :(得分:1)
Observable.FromAsync(token => restSharpClient.ExecuteTaskAsync(request, token))
.SelectMany(response =>
response.Data.HasCompleted
? Observable.Return(response)
: Observable.ThrowException(new Exception("Resource not ready."))
.DelaySubscription(TimeSpan.FromSeconds(5))
)
.Retry(10);
答案 1 :(得分:0)
我最终采用了TPL路线。我通过使用Task.Delay避免了显式的Thread.Sleep,它至少可以引用一个名称空间。
public class HttpRequestRetrierResponseContainer<T>
{
public T Value { get; set; }
public bool IsCriteriaMet { get; set; }
public int AttemptCount { get; set; }
}
public class HttpRequestRetrier
{
private readonly IHttpClientWrapper _httpClientWrapper;
public HttpRequestRetrier(IHttpClientWrapper httpClientWrapper)
{
_httpClientWrapper = httpClientWrapper;
}
public async Task<HttpRequestRetrierResponseContainer<T>> RepeatRequestUntilResponseMeetsCriteria<T>(Func<HttpRequestMessage> getMessage, Predicate<HttpResponseMessage> criteria, int maxAttempts)
{
return await Attempt<T>(getMessage, criteria, maxAttempts, 0);
}
private async Task<HttpRequestRetrierResponseContainer<T>> Attempt<T>(Func<HttpRequestMessage> getMessage, Predicate<HttpResponseMessage> criteria, int maxAttempts, int attemptCount)
{
if (attemptCount < maxAttempts)
{
var response = await _httpClientWrapper.SendMessageAsync(getMessage);
if (criteria(response))
{
var value = await response.Extensions().ReadAsAsync<T>();
return new HttpRequestRetrierResponseContainer<T> { IsCriteriaMet = true, AttemptCount = attemptCount, Value = value };
}
Task.Delay(500).Wait();
return await Attempt<T>(getMessage, criteria, maxAttempts, attemptCount + 1);
}
return new HttpRequestRetrierResponseContainer<T> { IsCriteriaMet = false };
}
}