【发布时间】:2018-11-14 03:24:14
【问题描述】:
我正在开发一个 .Net 核心解决方案,该解决方案从另一个微服务获取存储文件的备份,并且由于此过程需要很长时间,我们决定在后台任务下构建此例程。通过以下链接: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.1 我已经通过使用排队的后台任务来实现后台,如下所示:
public interface IBackgroundTaskQueue
{
void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem);
Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken);
}
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private ConcurrentQueue<Func<CancellationToken, Task>> _workItems =
new ConcurrentQueue<Func<CancellationToken, Task>>();
private SemaphoreSlim _signal = new SemaphoreSlim(0);
public void QueueBackgroundWorkItem(
Func<CancellationToken, Task> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
_workItems.Enqueue(workItem);
_signal.Release();
}
public async Task<Func<CancellationToken, Task>> DequeueAsync(
CancellationToken cancellationToken)
{
await _signal.WaitAsync(cancellationToken);
_workItems.TryDequeue(out var workItem);
return workItem;
}
}
public class QueuedHostedService : BackgroundService
{
private readonly ILogger _logger;
public QueuedHostedService(IBackgroundTaskQueue taskQueue,
ILoggerFactory loggerFactory)
{
TaskQueue = taskQueue;
_logger = loggerFactory.CreateLogger<QueuedHostedService>();
}
public IBackgroundTaskQueue TaskQueue { get; }
protected async override Task ExecuteAsync(
CancellationToken cancellationToken)
{
_logger.LogInformation("Queued Hosted Service is starting.");
while (!cancellationToken.IsCancellationRequested)
{
var workItem = await TaskQueue.DequeueAsync(cancellationToken);
try
{
await workItem(cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex,
$"Error occurred executing {nameof(workItem)}.");
}
}
_logger.LogInformation("Queued Hosted Service is stopping.");
}
}
}
在控制器操作方法中我做到了:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult TakeBackup()
{
// Process #1: update latest backup time in setting table.
var _setting = _settingService.FindByKey("BackupData");
var data = JsonConvert.DeserializeObject<BackUpData>(_setting.Value);
data.LatestBackupTime = DateTime.UtcNow;
_setting.Value = JsonConvert.SerializeObject(data);
_settingService.AddOrUpdate(_setting);
// Process #2: Begin a background service to excaute the backup task.
_queue.QueueBackgroundWorkItem(async token =>
{
// instead of this staff I will replace by the API I want to consume.
var guid = Guid.NewGuid().ToString();
for (int delayLoop = 0; delayLoop < 3; delayLoop++)
{
_logger.LogInformation(
$"Queued Background Task {guid} is running. {delayLoop}/3");
await Task.Delay(TimeSpan.FromSeconds(5), token);
}
_logger.LogInformation(
$"Queued Background Task {guid} is complete. 3/3");
// Here I need to redirect to the index view after the task is finished (my issue) ..
RedirectToAction("Index",new {progress="Done"});
});
return RedirectToAction("Index");
}
}
logger信息显示成功 我所需要的只是在后台任务成功完成后能够重新加载索引控制器,但由于某种原因我不知道它不能被重定向。
Index 操作方法是这样的:
public async Task<IActionResult> Index()
{
var links = new List<LinkObject>();
var files = await _storageProvider.GetAllFiles(null, "backup");
foreach (var f in files)
{
var file = f;
if (f.Contains("/devstoreaccount1/"))
{
file = file.Replace("/devstoreaccount1/", "");
}
file = file.TrimStart('/');
links.Add(new LinkObject()
{
Method = "GET",
Href = await _storageProvider.GetSasUrl(file),
Rel = f
});
}
return View(links);
}
谢谢!
【问题讨论】:
-
Webprogramming 和 Multitasking 不能很好地混合。页面生命周期要求页面尽快从内存中发送和删除。多任务处理会使它在内存中保存很长时间。 msdn.microsoft.com/en-us/library/ms178472.aspx您正在执行的任何多任务处理都应该是客户端,网页之外(并行运行的Web服务),或两者兼而有之。
-
你的“微服务”后台任务在哪里执行?是红隼产生这个过程吗?您的应用程序是否在 IIS 上运行?这是 Azure 函数还是其他远程托管函数?
-
我将通过 QueueBackgroundWorkItem 中的 http 客户端调用 API 或我需要的 API。我需要的是能够在队列完成后再次重新加载页面。显示后台进程已结束。
-
@JimYarbro ,我有一个名为 [Take Backup] 按钮的视图,通过单击它,将启动后台任务从外部微服务获取数据,一旦完成,我只需要更新用户备份任务已完成。我通过使用“进程正在进行中”的标签将其应用于视图语法,在后台任务完成后,我将使用新的查询字符串重新加载视图 return RedirectToAction("Index" ,新 {status="完成"});在剃须刀中,我将检测查询字符串并知道后台进程已完成。
-
@FreedomDeveloper 我相信你错过了我的问题。您的“外部微服务”在哪里运行?有关各种选项,请参阅我之前的问题。
标签: c# asp.net-core asp.net-core-mvc asp.net-core-webapi background-task