在 ASP.NET Core 上实现“Fire and Forget”方法的安全方法

2024-03-24

我正在尝试实现一个简单的日志记录库,该库将在多个项目中使用。库的工作是将 HTTP 请求发送到 ElasticSearch。这个库的要点是它不能等待响应。另外,我不关心任何错误/异常。它必须将请求发送到 ElasticSearch,并立即返回。我不想制作带有返回类型的接口Task,我希望他们留下来void.

下面是我的示例代码。这是“Fire and Forget”的正确且安全的实施吗?如果我用的话可以吗Task.Run()在高负载库中?或者我应该避免使用Task.Run()就我而言?另外,如果我不使用await with Task.Run(),我会阻塞线程吗? 这段代码在库中:

public enum LogLevel
{
    Trace = 1,
    Debug = 2,
    Info = 3,
    Warn = 4,
    Error = 5,
    Fatal = 6
}

public interface ILogger
{
    void Info(string action, string message);
}

public class Logger : ILogger
{
    private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler { Proxy = null, UseProxy = false });
    private static IConfigurationRoot _configuration;

    public Logger(IConfigurationRoot configuration)
    {
        _configuration = configuration;
    }

    public void Info(string action, string message)
    {
        Task.Run(() => Post(action, message, LogLevel.Info));
        /*Post(action, message, LogLevel.Info);*/ // Or should I just use it like this?
    }

    private async Task Post(string action, string message, LogLevel logLevel)
    {
        // Here I have some logic

        var jsonData = JsonConvert.SerializeObject(log);
        var content = new StringContent(jsonData, Encoding.UTF8, "application/json");

        var response = await _httpClient.PostAsync(_configuration.GetValue<string>("ElasticLogger:Url"), content);
        // No work here, the end of the method
    }
}

这是我在 Web api 的 Startup 类中的 ConfigureServices 方法中注册记录器的方法:

public void ConfigureServices(IServiceCollection services)
{
     // ......

     services.AddSingleton<ILogger, Logger>();

     // .....
}

此代码位于我的 Web api 内的方法中:

public void ExecuteOperation(ExecOperationRequest request)
{
    // Here some business logic

    _logger.Info("ExecuteOperation", "START"); // Log

   // Here also some business logic

    _logger.Info("ExecuteOperation", "END"); // Log
}

回复:对异步方法的意外调用与 Task.Run()

由于只有少量 CPU 密集型工作Post(即创建 json 有效负载),另一个没有任何好处Task.Run- 在我看来,在线程池上调度新任务的开销将超过任何好处。 IE。

Post(action, message, LogLevel.Info);*/ // Or should I just use it like this?

是两种方法中较好的一种。您可能希望抑制与未等待的任务相关的编译器警告,并为下一个遇到代码的开发人员留下评论。

但根据斯蒂芬·克利里的明确回答,开火后忘记ASP.Net 几乎从来都不是一个好主意 https://stackoverflow.com/q/36335345。最好是卸载工作,例如通过队列、Windows 服务、Azure Web 作业等。

还有额外的危险 - 如果未等待的任务抛出,你会想要观察异常 https://stackoverflow.com/q/3284137/314291.

另请注意,此后完成的任何工作Post(例如,如果您与response)这仍然是一个需要在线程池上安排的延续任务 - 如果您触发大量线程Post方法,当它们完成时你会遇到很多线程争用。

Re : 另外,如果我不将await 与Task.Run() 一起使用,我会阻塞线程吗?

await 不需要线程 https://blog.stephencleary.com/2013/11/there-is-no-thread.html. await是要求编译器异步重写代码的语法糖。Task.Run()将在线程池上安排第二个任务,该任务在到达线程池之前只会执行少量工作PostAsync方法,这就是为什么建议不使用它的原因。

来自未等待的调用的调用者线程使用量/阻塞量Info to Post取决于之前完成了什么样的工作Task被返回。 在您的情况下,Json 序列化工作将在调用者的线程上完成(我已标记为#1),但是与 HTTP 调用持续时间相比,执行时间应该可以忽略不计。所以虽然方法没有等待Info,HTTP 调用之后的任何代码仍然需要在 Http 调用完成时进行调度,并将调度到任何可用线程上 (#2)。

public void Info(string action, string message)
{
#pragma warning disable 4014 // Deliberate fire and forget
    Post(action, message, LogLevel.Info); // Unawaited Task, thread #1
#pragma warning restore 4014
}

private async Task Post(string action, string message, LogLevel logLevel)
{
    var jsonData = JsonConvert.SerializeObject(log); // #1
    var content = new StringContent(jsonData, Encoding.UTF8, "application/json"); // #1

    var response = await httpClient.PostAsync(...), content);

    // Work here will be scheduled on any available Thread, after PostAsync completes #2
}

回复:异常处理

try..catch块与异步代码一起使用 -await will 检查是否有故障Task https://stackoverflow.com/q/5383310/314291并引发异常:

 public async Task Post()
 {
     try
     {
         // ... other serialization code here ...
         await HttpPostAsync();
     }
     catch (Exception ex)
     {
         // Do you have a logger of last resort?
         Trace.WriteLine(ex.Message);
     }
 }

尽管上述内容符合观察异常的标准,但注册一个例外仍然是一个好主意UnobservedTaskException全局级别的处理程序。

这将帮助您检测并识别未能观察到异常的位置:

TaskScheduler.UnobservedTaskException += (sender, eventArgs) =>
{
    eventArgs.SetObserved();
    ((AggregateException)eventArgs.Exception).Handle(ex =>
    {
        // Arriving here is BAD - means we've forgotten an exception handler around await
        // Or haven't checked for `.IsFaulted` on `.ContinueWith`
        Trace.WriteLine($"Unobserved Exception {ex.Message}");
        return true;
    });
};

请注意,上述处理程序仅在任务被 GC 收集时才会触发,这可能是在异常发生后的一段时间。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 ASP.NET Core 上实现“Fire and Forget”方法的安全方法 的相关文章

随机推荐

  • 为什么SSIS创建这个任务失败?

    我编写了以下代码来实现通过 HTTP 获取文件的 SSIS 控制流任务 using System using Microsoft SqlServer Dts Runtime namespace HttpTask DtsTask Displa
  • 防止 ASP.NET Core 中拒绝服务 (DoS) 攻击的最佳实践

    我正在寻找有关 ASP NET Core Web 应用程序的拒绝服务 DoS 保护 缓解的最佳实践建议 指南 可能来自 Microsoft 到目前为止我发现的主要两个选项是 AspNetCore速率限制 https github com s
  • == 与 .NET 中的 Object.Equals(object)

    因此 当我还是个新手时 与现在的新手相比 我曾经认为这两个东西是彼此的语法糖 即使用一个而不是另一个只是个人喜好 随着时间的推移 我发现这两者不是一回事 即使在默认实现中也是如此 请参阅this http web archive org w
  • 是什么让Python3的打印函数线程安全?

    我在各种邮件列表和论坛上看到人们不断提到 Python 3 中的打印函数是线程安全的 根据我自己的测试 我认为没有理由怀疑这一点 import threading import time import random def worker l
  • 从引用创建shared_ptr

    我对 C 比较陌生 这似乎是一个菜鸟问题 但我无法使用互联网上的其他资源来解决它 我正在尝试从引用创建一个shared ptr 我有以下内容Book class include
  • 将“正常”std::string 转换为 utf-8

    让我们看看我是否可以在没有太多事实错误的情况下解释这一点 我正在编写一个字符串类 我希望它使用utf 8 存储在 std string 中 因为它是内部存储 我希望它能够同时 正常 std string and std wstring作为输
  • 如何仅删除一些方面标签?

    Using facet wrap 是否可以只删除一些方面标签 在下面的示例中 我希望物种标签仅出现在每行的第一列中 我知道我可以使用labeller功能 但不包括如何更改单个标签 data iris library tidyr librar
  • 在 firebase 中看不到请求标头的值?

    我执行以下操作 var headers new Headers headers append bunny test headers append rabbit jump fetch blahurl com someservice heade
  • 为什么 svcutil.exe 的 /reference 选项不起作用?

    我正在尝试使用 svcutil exe 从一组 wsdl 文件中仅生成服务契约 接口 当我这样做时 从 http 托管的 wsdl 它会获取包含的模式并为它们生成所有代码 Great 我会做什么REALLY然而 我喜欢做的是使用一组已经使用
  • SVG 线条动画不起作用

    I am trying to create an effect where smoke comes out of a vessel Like this example where smoke comes out of a coffee cu
  • 删除记录前检查规则违规情况

    我想为我的 SQL Server 2005 数据库中的许多实体实施 软删除 方案 我的意思是 如果没有违反引用完整性规则 我想从表中删除一行 否则我将在记录上设置一个标志以表示它已被删除 我希望强制执行此 软删除 模式的表必须将 无操作 应
  • 如何将两个小部件放置在同一行中?

    我有国家 地区代码 DropdownMenu 和电话 TextFormField 如何将它们放在同一级别 我尝试过对齐小部件 Row children
  • 更改 Android numberpicker 分隔线颜色

    我想将上面显示的数字选择器的分隔线颜色 蓝色 更改为透明 我尝试了很多事情 比如 number picker setDividerDrawable getResources getDrawable R color transparent n
  • “可选类型‘字符串’的值?”没有打开;您是想使用“!”吗?或者 '?' ?”

    我研究 iOS 或 Swift 的时间并不长 通过最新的 Xcode 更新之一 我在计算机上制作的许多应用程序现在似乎都在使用过时的语法 Xcode 告诉我们将其转换为新语法 但通常这并不能解决任何问题 而且我遇到了一个新问题 这是我在语法
  • CloudKit推送通知,应用程序在后台运行

    iOS8 2 app在后台运行时 不会收到任何推送通知 而如果它在前台运行 它可以很好地接收推送通知 知道发生了什么事吗 运行于CloudKit Development模式 订阅用于添加 编辑和删除 并使用以下内容didReceiveRem
  • DynamoDB ProjectionExpression 排除属性(除一个字段外的所有字段)

    我有一个要求 我的 dynamodb 表具有许多属性 并且我需要投影表达式中的所有属性 除了我不需要响应的一两列 我正在扫描桌子 有没有一种方法可以在 ProjectionExpression 中定义它 除了这一列 我见过一些示例 其中 P
  • 左外连接不返回主表中的所有记录

    当我执行左外连接时 我希望在添加连接表之前获取查询将返回的所有记录 但它仅返回与连接表匹配的记录 即 表中不存在 092387 的记录 文档 所以我只想为该记录的 文件名 字段返回 null 我做错了什么 mysql gt select d
  • Java Signature.verify 导致 SignatureException:签名编码错误 由 IOException 引起:序列标记错误

    首先 这不是一个重复的问题 因为大多数人在从缺少 BEGIN RSA CERTIFICATE 行的证书创建公钥时报告此异常 我想做的要点是 1 使用 SHA1withRSA 算法 RSA 密钥为 1024 位 在 JCOP 智能卡上签署 5
  • 使用 JavaScript 设置 HTML 页面垂直位置

    我有一个可以上下滚动的 HTML 页面 不是很多 但确实可以滚动 执行一些 JavaScript 后如何设置页面中的滚动位置 我正在使用 jQuery 在页面底部注入一些额外的 HTML 并且我想在添加新内容后以编程方式滚动到新内容的位置
  • 在 ASP.NET Core 上实现“Fire and Forget”方法的安全方法

    我正在尝试实现一个简单的日志记录库 该库将在多个项目中使用 库的工作是将 HTTP 请求发送到 ElasticSearch 这个库的要点是它不能等待响应 另外 我不关心任何错误 异常 它必须将请求发送到 ElasticSearch 并立即返