C# RestSharp-异步请求-应答模式

C# RestSharp-异步请求-应答模式,c#,rest,asynchronous,async-await,microservices,C#,Rest,Asynchronous,Async Await,Microservices,给出了以下情况: 新作业通过Post请求发送到API。此API返回作业ID和HTTP响应代码202 然后使用此作业ID请求状态终结点。如果端点在响应主体中设置了“Finished”属性,则可以继续执行步骤3 通过结果端点使用JobID查询结果,并可以对其进行处理 我的问题是我如何优雅而干净地解决这个问题。可能已经有现成的库实现了这个功能了吗?我找不到RestSharp或其他HttpClient的此类功能。 当前的解决方案如下所示: async Task PostNewJob() { var

给出了以下情况:

  • 新作业通过Post请求发送到API。此API返回作业ID和HTTP响应代码202

  • 然后使用此作业ID请求状态终结点。如果端点在响应主体中设置了“Finished”属性,则可以继续执行步骤3

  • 通过结果端点使用JobID查询结果,并可以对其进行处理

  • 我的问题是我如何优雅而干净地解决这个问题。可能已经有现成的库实现了这个功能了吗?我找不到RestSharp或其他HttpClient的此类功能。 当前的解决方案如下所示:

    async Task PostNewJob()
    {
    var restClient=新的restClient(“https://baseUrl/");
    var restRequest=新的restRequest(“工作”);
    //添加标题
    var response=wait restClient.ExecutePostTaskAsync(restRequest);
    字符串jobId=JsonConvert.DeserializeObject(response.Content);
    返回jobId;
    }
    异步任务WaitTillJobIsReady(字符串jobId)
    {
    string jobStatus=string.Empty;
    var-request=new-RestRequest(jobId){Method=Method.GET};
    做
    {
    如果(!String.IsNullOrEmpty(作业状态))
    Thread.Sleep(5000);//等待下一次状态更新
    var response=wait restClient.ExecuteGetTaskAsync(请求,CancellationToken.None);
    jobStatus=JsonConvert.DeserializeObject(response.Content);
    }while(作业状态!=“完成”);
    }
    异步任务GetJobResponse(字符串jobID)
    {
    var restClient=new restClient(@“Url/bulk/”+jobID);
    var restRequest=new restRequest(){Method=Method.GET};
    var response=wait restClient.ExecuteGetTaskAsync(restRequest,CancellationToken.None);
    dynamic downloadResponse=JsonConvert.DeserializeObject(response.Content);
    var responseResult=new List(){downloadsresponse?.ToList()};
    返回响应结果;
    }
    异步main()
    {
    var jobId=wait PostNewJob();
    WaitTillJobIsReady(jobID.Wait();
    var responseResult=等待GetJobResponse(作业ID);
    //处理结果
    }
    
    正如@Paulo Morgado所说,我不应该在生产代码中使用Thread.Sleep/Task延迟。但在我看来,我必须在WaitTillJobIsReady()方法中使用它?否则我会用循环中的Get请求压倒API


    针对此类问题的最佳做法是什么?

    长轮询

    有多种方法可以处理这种类型的问题,但正如其他人已经指出的那样,RestSharp这样的库目前没有内置这种方法。在我看来,克服这一问题的首选方法是修改API以支持Nikita建议的某种类型的应用程序。这就是:

    服务器保持请求打开,直到有新数据可用。一旦 如果可用,服务器将响应并发送新信息。当 客户机收到新信息后,会立即发送另一条信息 请求,然后重复该操作。这有效地模拟了 服务器推送功能

    使用调度程序

    不幸的是,这并不总是可能的。另一个更优雅的解决方案是创建一个检查状态的服务,然后使用诸如或之类的调度器,以重复出现的间隔(如500ms到3s)调度服务,直到成功为止。一旦它返回“Finished”属性,您就可以将任务标记为complete,以停止进程继续轮询。这可以说比您当前的解决方案更好,并且可以对正在发生的事情提供更多的控制和反馈

    使用计时器

    除了使用线程。睡眠,更好的选择是使用线程。这将允许您在指定的时间间隔连续调用委托,这似乎是您在这里想要做的

    下面是计时器的使用示例,它将每2秒运行一次,直到达到10次运行。(摘自)

    使用系统;
    使用系统线程;
    使用System.Threading.Tasks;
    班级计划
    {
    专用静态定时器;
    静态void Main(字符串[]参数)
    {
    var timerState=new timerState{Counter=0};
    定时器=新定时器(
    回调:新的TimerCallback(TimerTask),
    州:timerState,
    决斗时间:1000,
    期间:2000年);
    
    while(timerState.Counter)在
    async
    code中不使用
    Thread.Sleep(…)
    。使用
    wait Task,Delay(…)
    取而代之。在生产代码中,不使用它们。使用
    HttpClient
    而不是RestSharp。不幸的是,这并没有完全回答我的问题。如果没有线程。睡眠(…)或任务。延迟(…),我应该如何解决作业状态的轮询问题在高效的代码中?我希望节省使用GET-Requests?基本上您正在做的事情已经可以了-它被称为轮询(虽然轮询时间不长,因为您没有打开一个流)。是的,您可能需要对其进行一点重构,但这个想法是正确的,而且就我而言,没有任何库提供现成的功能。您可以在这里看到类似的内容—令人惊讶的是,没有人提到Polly。这是一个非常有用的库,用于实现重试逻辑。它有多种用途,但最简单的一种是处理响应和specify是否重试以及在重试之前等待多长时间。也就是说,正如Nikita所说,轮询很好,我通常是最简单的解决方案。Task.Delay与Thread.Sleep不同,它会将资源释放到线程池,因此我会尽可能使用Task.Delay。
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    class Program
    {
        private static Timer timer;
    
        static void Main(string[] args)
        {
            var timerState = new TimerState { Counter = 0 };
    
            timer = new Timer(
                callback: new TimerCallback(TimerTask),
                state: timerState,
                dueTime: 1000,
                period: 2000);
    
            while (timerState.Counter <= 10)
            {
                Task.Delay(1000).Wait();
            }
    
            timer.Dispose();
            Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}: done.");
        }
    
        private static void TimerTask(object timerState)
        {
            Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}: starting a new callback.");
            var state = timerState as TimerState;
            Interlocked.Increment(ref state.Counter);
        }
    
        class TimerState
        {
            public int Counter;
        }
    }