简体   繁体   English

在 ASP.Net CORE Web 请求上执行异步工作时将文件保存在磁盘上的最有效方法

[英]Most efficient way to save a file on disk while executing async work, on a ASP.Net CORE Web Request

On a request on my Web API, I'm saving a image to the disk and also processing it with an external API which usually takes several seconds.根据我的 Web API 的请求,我将图像保存到磁盘并使用外部 API 处理它,这通常需要几秒钟。 This is a high traffic API, thus I would like to design it in the most efficient way.这是一个高流量的 API,因此我想以最有效的方式设计它。 The image comes in Base64 "encoding", but this is not pertinent.该图像采用 Base64“编码”,但这不相关。 We can think about it as an arbitrary byte array of 150KB in average (so the saving to disk operation should be really fast) .我们可以将其视为平均 150KB 的任意字节数组(因此保存到磁盘的操作应该非常快)

The workflow is (the first two operations don't need to execute in any order, obviously):工作流程是(显然,前两个操作不需要按任何顺序执行):

  • Save image on disk (async)将图像保存在磁盘上(异步)
  • Process it on an external API (async)在外部 API(异步)上处理它
  • Return Ok in case of both previous operations succeed如果之前的两个操作都成功,则返回 Ok

With that in mind, I put together this (simplified) code:考虑到这一点,我把这个(简化的)代码放在一起:

public async Task<IActionResult> Post([FromBody] string imageBase64)
{
    // Convert Image
    byte[] imageByteArray = Convert.FromBase64String(imageBase64);

    // Start async Task to Save Image
    Task taskSaveImage = System.IO.File.WriteAllBytesAsync(@"C:\ImagesStorage\image.jpg", imageByteArray);

    // Execute some heavy async processing
    await ProcessOnExternalAPI(imageByteArray);

    // Guarantee that Save Image Task has completed
    await taskSaveImage;

    // Return 200 Ok
    return Ok();
}

This code seems to me the most efficient way to save the image on disk, and also process it with the external API, both at the same time while not blocking the ASP.Net CORE working thread .在我看来,这段代码是将图像保存在磁盘上的最有效方法,并且还使用外部 API 处理它,同时不阻塞 ASP.Net CORE 工作线程 Is that right, or there is some more efficient way to do it?这是正确的,还是有一些更有效的方法来做到这一点?

Also, there is any problem to share the byte[] imageByteArray object between the two Tasks (hence possibly two threads)?此外,在两个任务(因此可能是两个线程)之间共享byte[] imageByteArray object 有什么问题吗? I believe that.Net CORE would take care of it, but I would not be happy to discover that i'm wrong once in production.我相信.Net CORE 会处理它,但我不会很高兴在生产中发现我错了。

  • Obs 1: I know that receiving a stream for the byte array may lead to a better performance than receiving a Base64 encoded byte array. Obs 1:我知道接收字节数组的 stream 可能比接收 Base64 编码的字节数组具有更好的性能。 I have no control over that.我对此无能为力。 It's how the API has to be. API 必须如此。
  • Obs 2: The request to the external RESTful API (inside the ProcessOnExternalAPI method above) is made using the async method PostAsync from System.Net.HttpClient class. Obs 2:使用System.Net.HttpClient class 中的async方法PostAsync向外部 RESTful API(在上面的ProcessOnExternalAPI方法中)发出请求。
  • Obs 3: My biggest concern is always have Working Threads to respond to the requests. Obs 3:我最关心的是总是有工作线程来响应请求。 This is not the only service that my API respond to.这不是我的 API 响应的唯一服务。
  • Obs 4: I also checked out this question/answer on SO: Using async/await for multiple tasks But over there, there is no concern about the asp.net core working threads, which is my main concern. Obs 4:我还查看了关于 SO: Using async/await for multiple tasks但是在那边,没有关于 asp.net 核心工作线程的问题,这是我主要关心的问题。
  • Obs 5: The code is made based on my humble knowledge of async programming on ASP.Net CORE, and also relying on the this Microsoft's Documentation: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/ That said, I would love to hear from someone with more experience on async, on this type of scenario. Obs 5:代码是基于我对 ASP.Net CORE 异步编程的浅薄知识编写的,并且还依赖于 Microsoft 的文档: https://docs.microsoft.com/en-us/dotnet/csharp/programming- guide/concepts/async/也就是说,在这种情况下,我很想听听在异步方面有更多经验的人的意见。

Is that right, or there is some more efficient way to do it?这是正确的,还是有一些更有效的方法来做到这一点?

In terms of threads, that is correct: your code is doing two concurrent asynchronous operations.就线程而言,这是正确的:您的代码正在执行两个并发异步操作。

I do prefer using Task.WhenAll , since that makes the intent of the code more explicit (and also handles an edge case where the write-to-disk task can become fire-and-forget, as noted by Theodor in the comments):我确实更喜欢使用Task.WhenAll ,因为这使代码的意图更加明确(并且还处理了写入磁盘任务可能成为即发即忘的边缘情况,正如 Theodor 在评论中指出的那样):

public async Task<IActionResult> Post([FromBody] string imageBase64)
{
  // Convert Image
  byte[] imageByteArray = Convert.FromBase64String(imageBase64);

  // Start async Task to Save Image
  Task saveImageTask = System.IO.File.WriteAllBytesAsync(@"C:\ImagesStorage\image.jpg", imageByteArray);

  // Start task to call API
  Task processTask = ProcessOnExternalAPI(imageByteArray);

  // Asynchronously wait for both tasks to complete.
  await Task.WhenAll(saveImageTask, processTask);

  // Return 200 Ok
  return Ok();
}

Also, there is any problem to share the byte[] imageByteArray object between the two Tasks (hence possibly two threads)?此外,在两个任务(因此可能是两个线程)之间共享 byte[] imageByteArray object 有什么问题吗? I believe that.Net CORE would take care of it, but I would not be happy to discover that i'm wrong once in production.我相信.Net CORE 会处理它,但我不会很高兴在生产中发现我错了。

No, no problems there.不,那里没有问题。 It's safe to share values that don't change.分享不变的价值观是安全的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM