【问题标题】:C # HttpWebRequest , Fire and forget an API callC# HttpWebRequest,触发并忘记 API 调用
【发布时间】:2021-12-13 00:26:22
【问题描述】:

在我的 WCF 服务中,我必须调用一个 API,我想在其中执行 Fire and Forget 实现。如果可能的话,只要捕获任何错误。(这也很好,如果不是一个选项)

我计划进行以下实施,它可能导致什么问题?通过执行以下实现将留下大量打开的连接。或者可能是什么问题?请帮助了解如何以更好的方式实现这一点。

void SendRequest(inputs)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
    request.Method = "POST";
    request.ContentType = "application/xml";

    byte[] requestBytes = Encoding.UTF8.GetBytes(inputXML);
    using (Stream requestStream = request.GetRequestStream())
    {
        requestStream.Write(requestBytes, 0, requestBytes.Length);
    }

    request.GetResponseAsync();
} 
Main()
{ 
    try
        SendRequest(inputs);
    catch ex
        log ex;
}

【问题讨论】:

  • 您有权自己回答所有这些问题。运行你的代码,看看会发生什么。
  • 感谢您的链接,我正在尽力理解。是的,我确实尝试了上述方法,它似乎正在工作,但担心是否存在一些我可能不知道的问题/影响。所以希望这里的专家分享他们对这种方法的看法。

标签: c# wcf httpwebrequest fire-and-forget


【解决方案1】:

首先,制作完全异步的代码版本

using System.Threading;

public async Task<System.Net.WebResponse> SendRequestAsync(
    string inputXML, string url, CancellationToken cancellationToken)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = "POST";
    request.ContentType = "application/xml";

    byte[] requestBytes = Encoding.UTF8.GetBytes(inputXML);

    // GetRequestStreamAsync is a lightweight operation I assume
    // so we don't really need any cancellation
    using (Stream requestStream = await request.GetRequestStreamAsync())
    {
        // Idk if we really need cancellation here, but just in case of big request we might need to
        await requestStream.WriteAsync(
            requestBytes, 0, requestBytes.Length, cancellationToken);
    }
    
    // Close any long-running request
    using (cancellationToken.Register(() => request.Abort(), useSynchronizationContext: false))
    {
        var response = await request.GetResponseAsync();
        cancellationToken.ThrowIfCancellationRequested();
        return response;
    }
} 

让我们创建一个async void 方法,但要确保它安全。它基本上会以“即发即弃”的方式执行。

public async void DoWork(string inputXML, string url, CancellationToken ct)
{
    try
    {
        using(var response = await SendRequestAsync(inputXML, url, ct))
        {
            var httpResponse = (HttpWebResponse) response;
            // Use 201 Created or whatever you need
            if (httpResponse.StatusCode != HttpStatusCode.Created)
            { 
                // TODO: handle wrong status code
            }
        }
    }
    catch (Exception e)
    {
        if (ct.IsCancellationRequested)
        {
            Console.WriteLine("Cancelled");
        }
        else
        {
            // TODO: handle exception
        }
    }
}

private static CancellationTokenSource _cts = new CancellationTokenSource();

public static void Main (string[] args)
{
    DoWork("xml string", "example.com", cts.Token);
    Console.WriteLine("Boom!");
    if (Console.ReadLine() == "exit")
    {
        // Cancel the damn job
        cts.Cancel();
    }
}

DoWork 中处理所有错误很重要,因为跟随将不起作用

// Warning, will NOT catch an exception
public static void Main (string[] args)
{
    try 
    {
        DoWork("xml string", "example.com"); 
    }
    catch (Exception e)
    {
    }
}

编辑:OP 要求取消,所以我添加了取消

【讨论】:

  • 所有这些更改都有意义,我现在尝试一下,并将更新您的状态。只是想知道即使调用线程将被关闭,这仍然可以工作。例如,我的 WCF 服务被调用,它通过上述建议的方法调用了一个 API,让我们假设 API 需要 2 分钟。因此在这种情况下,即使调用线程在调用此方法后立即关闭,API 调用也不会立即终止
  • @manasdas 很好的问题。将为我的答案添加取消功能
  • 谢谢。因此,如果我决定这样做,即使我的调用方法(已调用 WCF 服务的操作)已完成,我也不必取消 API 调用。那我也可以使用您建议的先前版本吗?对于这么多问题,我深表歉意,我只是想更好地理解它,而这些问题是在不同场景的测试过程中出现的。
  • 如果您什么都不想做,请将CancellationToken.None 传递给DoWork,如果您想在某处手动取消它,请致电cts.Cancel()。 API 调用将在两种情况下运行到最后:您传递 CancellationToken.None 或只是不取消。
  • 事情在没有取消实现的情况下工作,只是在 API URL 之类的情况下的异常无法解析或某些..那些没有被捕获。对此有任何想法
【解决方案2】:

请注意,最好不要使用即弃即忘,尤其是如果这是应用程序的核心层,您可能会错过重要的例外情况。当您使用此技术时,您必须记住会发生以下情况:

  1. 异常将无声地失败,没有任何机会捕获它们。通常你会想要记录它们或收到通知。
  2. 您不知道代码何时完成,
  3. 由于您不需要代码来完成并且它可能无法运行给您,因此您不会收到无法完成的通知。

使用此技术的一个很好的案例可能是更新缓存,例如。

话虽如此,您可以使用以下技术:

NET 4.5 允许我们通过Task.Run 使用它

Task.Run(() => FireAndForget());

您也可以使用无参数 lambda 启动线程:

(new Thread(() => { 
 FireAndForget(); 
 }) { 
   Name = "Running Work Thread (FireAndForget)",
   Priority = ThreadPriority.BelowNormal 
}).Start();

【讨论】:

  • 我也会尝试这样做,以了解可用于实现相同行为的所有不同方法。
猜你喜欢
  • 2020-04-25
  • 1970-01-01
  • 2010-09-14
  • 1970-01-01
  • 1970-01-01
  • 2014-12-06
  • 1970-01-01
  • 1970-01-01
  • 2021-04-17
相关资源
最近更新 更多