C# 馈线应用程序的多线程体系结构

C# 馈线应用程序的多线程体系结构,c#,sql-server,multithreading,csv,task-parallel-library,C#,Sql Server,Multithreading,Csv,Task Parallel Library,这是我在这里的第一篇文章,如果结构不好,我深表歉意 我们的任务是设计一种工具,该工具将: 读取CSV格式的(帐户ID)文件 从web下载每个帐户的帐户数据文件(按Id)(REST API) 将文件传递给转换器,转换器将生成报告(财务预测等)[~20ms] 如果预测阈值在限制范围内,请运行解析器分析数据[400ms] 为上述分析生成报告[80ms] 将生成的所有文件上载到web(REST API) 现在,所有这些单独的点都相对容易做到。我感兴趣的是找出如何最好地设计一些东西来处理这个问题,并在

这是我在这里的第一篇文章,如果结构不好,我深表歉意

我们的任务是设计一种工具,该工具将:

  • 读取CSV格式的(帐户ID)文件
  • 从web下载每个帐户的帐户数据文件(按Id)(REST API)
  • 将文件传递给转换器,转换器将生成报告(财务预测等)[~20ms]
  • 如果预测阈值在限制范围内,请运行解析器分析数据[400ms]
  • 为上述分析生成报告[80ms]
  • 将生成的所有文件上载到web(REST API)
现在,所有这些单独的点都相对容易做到。我感兴趣的是找出如何最好地设计一些东西来处理这个问题,并在我们的硬件上快速高效地完成它

我们必须处理大约200万个账户。方括号给出了每个过程平均花费的时间。我想使用机器上可用的最大资源-24核Xeon处理器。这不是一个内存密集型的过程

使用第三方物流并将其作为一项任务创建是一个好主意吗?每一个都必须按顺序进行,但很多都可以同时进行。不幸的是,解析器不支持多线程,我们也没有源代码(对我们来说,它本质上是一个黑盒子)

我的想法是这样的——假设我们正在使用第三方物流:

  • 加载帐户数据(本质上是CSV导入或SQL选择)
  • 对于每个帐户(Id):
    • 下载每个帐户的数据文件
    • 继续使用数据文件发送到转换器
    • 继续检查阈值,发送到解析器
    • 继续生成报告
    • 继续上载输出
这听起来是可行的还是我没有正确理解?用不同的方式分解这些步骤会更好吗

我有点不确定如何处理解析器抛出异常的问题(这很挑剔),或者当我们上传失败时


所有这些都将在一个计划作业中进行,该作业将在数小时后作为控制台应用程序运行。

我认为在您的情况下,从多线程开始的简单方法是将每个帐户id的整个操作放在一个线程中(或者更好,放在一个线程池中)。按照下面建议的方式,我认为您不需要控制线程间操作

将数据放入线程池队列的方法如下:


我会考虑使用某种消息总线。因此,您可以将这些步骤分开,如果其中一个步骤不起作用(例如,因为REST服务在一段时间内无法访问),您可以存储消息以供以后处理

根据您作为messagebus使用的内容,您可以使用它引入线程

在我看来,如果您有更高级别的抽象(如服务总线),您可以更好地设计工作流、处理异常状态等

此外,如果部件可以独立运行,它们不会相互阻塞

一个简单的方法是使用Redis ServiceBus

这里引用了一些优点:

  • 基于消息的设计允许更容易的并行化和计算的内省

  • DLQ消息可以在服务器更新和重新加入正常消息工作流后进行内省、修复和稍后重播


我应该指出,我们正在使用.NET4.5,我正在阅读TPL数据流,这似乎是一个前进的方向。不幸的是,在这个阶段Messagebus不是一个选项。Messagebus是一种方式,因为它是一个概念——猜猜看,数据流在内部也使用消息总线。数据流可能是实现这一点的一个好方法。重要的是不要将像NMessageBus这样的库和conecpt it self混淆:)有真正轻量级的MessageBus
var accountIds = new List<int>();
foreach (var accountId in accountIds)
{
    ThreadPool.QueueUserWorkItem(ProcessAccount, accountId);
}
public static void ProcessAccount(object accountId)
{
    // Download the data file for this account
    // ContinueWith using the data file, send to the converter
    // ContinueWith check threshold, send to parser
    // ContinueWith Generate Report
    // ContinueWith Upload outputs
}