【问题标题】:System Design / Architecture Best Approach系统设计/架构最佳方法
【发布时间】:2010-12-30 23:39:46
【问题描述】:

我有一个系统,它有 3 个通用部分来帮助我进行描述。

1) DATABASE- 存储所有表,同一个数据库将存储其他服务的数据,包括 Web 应用程序、silverlight 等...(需要灵活,如果在远程服务器,可以通过 web 服务公开,如果是本地的,可以在本地或通过 TCP 连接到 windows 服务)

2) BLACK BOX - 一次处理一个项目,方法是从数据库中注入所需项目的列表,如管道,您可以在其中输入一组条件、单个项目的值项,并返回该单个处理项的结果。

3) WINDOWS SERVICE- 从数据库中检索数据,注入黑盒,以预定义的时间间隔将结果从黑盒保存到数据库。该服务可能与数据库位于不同的服务器上。如果发生错误,将记录错误并继续。

平均而言,windows 服务将需要处理大约 5000 个项目,黑盒大约需要 1.5 秒来处理 5000 个项目。

我的问题是:

a) Windows 服务应该从数据库中获取要处理的项目的批处理列表,还是应该获取 id 列表,并在传递到黑盒之前循环从数据库中获取每个单独的项目?请注意,其他应用程序也正在使用相同的数据库。试探性地,我猜数据库应该是某种网络服务调用。

b) 是否应在处理后立即保存单个项目?还是应该等待批处理完成后再保存?当系统在处理过程中突然出现故障时,在处理后保存每个单独的项目是好的,至少保存处理的项目,但由于它对 Web 服务的 5000 次调用而以性能为代价?

关于最佳解决方案的任何建议?

干杯

【问题讨论】:

  • 您可以考虑使用 MSMQ 队列,一次处理一项,然后在处理完成后将其删除吗?
  • 我对MSMQ不是很熟悉,能否简单介绍一下它是如何适用的?
  • msmq 将保证物品的交付。不确定它在这里是否完全适用,但它可能很有用,但它会使设计复杂化

标签: .net web-services architecture windows-services soa


【解决方案1】:
  1. 您应该批量提取项目,以免请求阻塞网络。获取一个 ID 列表,然后循环它们并每次提取完整的项目是 N 次额外的数据库调用。

    • 如果您认为您将从抽象中受益,您可以使用 Web 服务来处理调用数据库。否则只会造成不必要的复杂性。

  2. 在完成每个项目时更新数据库。成品一旦准备好就可以在生产线上进一步使用,而不必等待 5000 批完成。

    • 这假设您将为每个项目保存数据

    • 无论如何,您都需要进行 N 次调用(以保存每个项目),因此通过等待然后在每批结束时进行更新不会获得太多收益。

    • 如果崩溃,您将丢失所有未保存的数据。

    • 如果您不需要存储黑盒中的每项结果,那么您有充分的理由考虑将所有内容作为一个批次进行更新。


我已经为一家银行编写了一堆这样的应用程序。我常用的方法如下——简单、容错、高效。 (假设您需要处理一组项目并为每个项目保存数据)

  1. 除了 items 表之外,数据库还有一个表示处理项目状态的表。预先做一些额外的工作,这将使调试和审核过程变得轻而易举:

    table ItemsProcessStatus  -- feel free to improve upon the name
    int orderID (auto increment)
    int itemID  (fk to items)
    datetime pulledForProcessing null
    datetime finishedProcessing null
    ..etc
    
  2. windows 服务 在计时器上运行,例如每 X 分钟一次,并拉取 limit(Y) 项进行处理。这会在ItemsProcessStatus 表中用时间戳标记pulledForProcessing 标志。

    • 您想拉取拉取日期为空的项目[以及那些已拉取但未完成且早于 Z 分钟的项目(我通常选择 15 到 30 分钟)]

    • 小心拉动这些的过程。你需要使用锁

    • 您可以进一步细化:在第一次迭代中,抓取 Y 项目,其中 Y 是对您在该时间跨度内可以处理多少的正确猜测。下一次迭代,您计算它正​​在处理的速率(作为滑动平均值)并调整要提取的项目数。这样,它将不断调整自身以满负荷处理。

  3. windows 服务通过将它们发送到黑匣子来一一处理这些(嗯,通常它是多线程的,一次处理这么多)。

    • 我将它们放入线程安全队列(不要与 msmq 混淆)。工作线程循环,从队列中拉取,处理黑箱中的项目,然后更新数据库。

    • 您可以在这里使用任何典型的多线程技术(等待/脉冲、读取器/写入器锁苗条、等待句柄),或者如果队列为空,则让工作线程休眠几秒钟

  4. 在每个项目完成后,调用该项目的更新过程,这也会更新 ItemsProcessStatus 表(表示它已完成处理)

  5. 当您的服务停止时,完成所有正在处理的项目的处理并在数据库中更新它们。

    • 对于所有尚未发送到黑匣子的项目,您可以通过将pulledForProcessing 设置为空来取消它们在进程表中的标记。

  6. 如果您的服务崩溃,您不会“丢失”很多数据。未标记的项目将在超过一定年龄时再次被拉出(进程表)


这适用于安装在一组服务器上的 Windows 服务的多个实例(尽管您需要将 ComputerName 添加到进程表中以识别每个服务正在运行的计算机)。这是有效的,因为每个服务只是抓取“下一组项目”来处理——不需要任何类型的路由或进程相互通信。

【讨论】:

  • 太棒了,谢谢,我认为这是一个非常合理的解决方案,而且确实有意义。
  • 哎哟。不是一个好的解决方案——队列是一种标准的做事方式,在某种程度上比网络服务器、应用程序服务器、数据库更重要……就实现良好而言……它就像神经系统整个系统。你不想自己动手。
  • 他对每个项目的“黑盒”处理取决于数据库的状态。因此,如果数据库出现故障,无论他是否使用消息队列,他都无法再处理。如果他可以重新处理完全独立于数据库的处理,那么使用队列将是有意义的。
【解决方案2】:

MSMQ 是 Microsoft 的排队方法。我同意应该使用排队方法——这是在大多数处理大量事务的系统中完成的。例如,在我以前工作的银行,我们使用 MQ 作为我们的中间件解决方案。

优点是流程中的下一步可以在第一步之后立即开始处理,而无需等待所有 5000 个条目都被处理完。如果这个数字增加到 500,000,000 怎么办?然后第一个项目完成的等待时间将大大增加。使用排队的方法,它根本不会改变。

还有其他优势 - 可扩展性、稳健性、保证交付等 - 但您可以稍后了解这些问题。

此外,一个良好实现的队列在使用它的进程中产生的等待开销非常小,因为它们几乎总是支持多个线程访问队列。 (会有开销,但不会大大增加等待时间)。

【讨论】:

  • 仍然不太确定排队会有什么帮助,事实上,我是在我的windows服务中循环排队作业,或者我可以在服务中进行多线程,有什么区别?只有在处理之后,我才会将处理后的状态及其结果、计算时间等保存到数据库中……所以如果 MSMQ 位于中间,在 Windows 服务和数据库之间,我的问题 a) 和 b)仍然站立。并在队列中等待时间 = 访问数据库以获取所需的项目?因为处理黑匣子中的一项非常快。
  • 这样说吧,不知道 MSMQ 是如何工作的,但它不等于 5000 个项目 = 至少 5000 次检索/服务调用(如果不分批且不包括与项目一起检索的其他条件) + 5000 次保存调用(如果不是批处理),因为每个队列 = 每个项目。
  • 我也在一家银行使用过 msmq :) 虽然他们使用它的方式,但它总是更多的是问题而不是解决方案
  • 队列更加灵活。如果系统的某个组件出现故障一小时,您的系统能否正常工作而不会丢失任何交易?
  • 从 MSMQ 获得的一个好处是吞吐量显着提高。在那里转储数据比写入数据库更快。如果您的数据库服务器已关闭,只要您的队列处于运行状态,您仍然可以继续处理。另一个好处是您不会在数据库中存储半生不熟的数据。如果它没有被处理,为什么它会被持久化?这是您将数据转储到队列中的地方,让您的队列工作人员处理它并保留它。如果不能,它可以将数据移动到“毒药”队列,您可以在其中运行另一个服务来评估错误数据并对其进行处理(工作流程?)
猜你喜欢
  • 2012-02-24
  • 2013-12-20
  • 1970-01-01
  • 1970-01-01
  • 2022-08-20
  • 2017-03-10
  • 1970-01-01
  • 2020-04-28
  • 2011-10-05
相关资源
最近更新 更多