【问题标题】:How to make active services highly available?如何让主动服务高可用?
【发布时间】:2011-02-09 00:01:06
【问题描述】:

我知道通过Network Load BalancingFailover Clustering我们可以使被动服务具有高可用性。但是活跃的应用程序呢?

示例:我的一个应用程序以固定的时间间隔从外部资源中检索一些内容。我设想过以下场景:

  1. 在一台机器上运行它。问题:如果这个实例下降,内容将不会被检索到
  2. 在集群的每台机器上运行它。问题:内容将被多次检索
  3. 在集群的每台机器上都有它,但只在其中一台机器上运行它。每个实例都必须检查某种公共资源来决定是否轮到它来执行任务。

当我在考虑解决方案 #3 时,我想知道什么应该是公共资源。我曾想过在数据库中创建一个表,我们可以使用它来获取全局锁。

这是最好的解决方案吗?人们通常如何做到这一点?

顺便说一下,它是在 Windows Server 2008 上运行的 C# .NET WCF 应用程序

【问题讨论】:

    标签: .net windows-server-2008 load-balancing high-availability failovercluster


    【解决方案1】:

    针对此类问题,他们发明了消息队列。想象一下,当您的集群应用程序都侦听消息队列(集群自身:-))时的情况。在某个时间点,一个实例会收到您的初始命令来下载您的外部资源。如果成功,您的实例会刷新消息,而是发布另一条消息,以便稍后执行时间等于“运行时间”+“间隔”。但万一实例在处理过程中死亡,这不是问题。消息在队列中回滚(超时后),其他一些实例可以拾取它。一点事务,一点消息队列

    我在世界的 Java EE 方面,因此可以帮助您了解编码细节

    【讨论】:

    • up-vote b/c 这是一个很好的模式,但是我认为您的答案不太适用于 OP,因为他正在查看特定于 NLB 和集群的可用性选项,而不是企业拱门。
    • 看看 Amazon Simple Queue Service,您可以使用类似的实现(甚至购买他们的服务)。
    【解决方案2】:

    在某些情况下,人们发现让 3 台机器处理所有请求很有用,然后在最后比较结果,以确保结果绝对正确,并且在处理它时没有硬件故障导致任何问题。这就是他们在飞机上所做的事情。

    在其他时候,您可以忍受一个糟糕的结果和短暂的停机时间来切换到新服务,但只希望下一个没问题。在这种情况下,带有心跳监视器的 3 号解决方案是一个很好的设置。

    在其他时候,人们只需要通过 SMS 通知他们的服务已关闭,并且应用程序只会使用一些过时的数据,直到您手动执行某种故障转移。

    在你的情况下,我想说后者可能对你更有用。由于您不能真正依赖另一端可用的服务,因此您仍然必须想出一个解决方案来解决这种情况。回馈过时的数据可能对您有好处,也可能不是。很抱歉不得不说:这取决于。

    【讨论】:

    • 我已经确定解决方案 3 适合我,我不确定的是同步方法。
    • 这个问题没有提到正在检索什么类型的内容,但它可能是一个安全的假设,它会随时间而变化(例如股票报价),并且可能无法保证 3 个服务器发出请求不同的时间会收到相同的数据。
    • @Tuzo 在我的情况下,数据仅每 2 分钟更新一次,并且每 1 分 50 秒获取一次
    【解决方案3】:

    我曾经使用您的解决方案 #3 实现了类似的东西。

    创建一个名为resource_lock 的表,其中包含一个包含锁定键的列(例如locking_key)。

    然后在每个时间间隔,您应用的所有实例都会:

    1. 运行类似“update resource_lock set resource_key = 1 where resource_key is null”的查询。 (您当然也可以插入特定于服务器的 id、时间戳等)
    2. 如果更新了 0 行:什么都不做 - 另一个应用实例已经在获取资源。
    3. 如果更新了 1 行:获取资源并将 locking_key 设置回 null

    这样做有两个好处:

    • 如果您的其中一台服务器出现故障,仍在运行的服务器仍会获取资源。
    • 您将锁定留给数据库,这样您就不必自己实现它。

    【讨论】:

    • 如果在执行过程中出现故障怎么办?
    • 然后问问自己:期望再次尝试时会成功获取资源是否现实?如果是:实现某种重试机制。如果不是:跳过并等待下一个间隔。我想这也取决于每次获取资源的重要性。
    • 我在询问行值。如果将其更新为 1 的进程停止,则该值可能会保持不变,并且不会有进程再次获取该资源。
    • 好吧,当然你应该总是有某种finally块来释放资源。但你的意思是某种你无法恢复的崩溃?在这种情况下,时间戳可能比零和一更好。然后检查锁的条件可以例如是(resource_key 为 null 或 resource_key
    【解决方案4】:

    从简单的角度来看,完成您正在寻找的最快/最简单的方法是“循环”您的集群,以便为每个请求选择一台机器(通过集群管理服务或一些这样的)来处理请求。实际的客户端请求不会直接发送到处理它的机器;相反,它们指向单个端点,该端点充当代理,根据可用性和负载将传入请求分发到机器。引用下面引用的链接,

    网络负载平衡是一种配置机器池的方法,以便它们轮流响应请求。它最常见于服务器群中实现:配置相同的机器,为网站分散负载,或者可能是终端服务器群。您也可以将它用于防火墙 (ISA) 场、vpn 接入点,实际上,任何时候您的 TCP/IP 流量对于单台机器来说已经成为过多负载,但您仍然希望它显示为单台机器访问目的。

    至于您的应用程序处于“主动”状态,该要求不会影响此等式,因为无论是“主动”还是“被动”,应用程序仍会向您的服务器发出请求。

    存在用于服务 HTTP 样式请求的商业负载平衡器,因此可能值得研究,但使用 W2k8 的负载平衡功能,您可能最好利用这些功能。

    有关如何在 Win2k8 中配置的更多信息,请参阅this 文章。

    this article 技术性更强,专注于将 NLB 与 Exchange 结合使用,但这些原则仍应适用于您的情况。

    see here 了解 NLB 设置和配置的另一个详细演练。

    否则,您可能会通过在 ServerFault 上搜索/发布得到很好的服务,因为您的应用程序代码没有(也不应该)严格知道 NLB 甚至存在。

    编辑:添加了另一个链接。

    编辑(第二次):OP 纠正了我在“主动”与“被动”概念中的错误结论。我对此的回答与我最初的回答非常相似,除了“活动”服务(由于您使用的是 WCF,很容易成为 Windows 服务)可以分为两部分:实际处理部分和管理部分。管理部分将在单个服务器上运行,并充当其他服务器执行实际处理的循环负载平衡器。它比原始场景稍微复杂一些,但我相信它会提供很大的灵活性,并在处理和管理逻辑之间提供清晰的分离。

    【讨论】:

    • 你不明白我所说的主动是什么意思。在活动场景中,我的服务器不会收到任何请求。相反,他们会生成它。
    • 但我对您的解决方案有疑问。就我而言,“管理部分”已经分开了,我担心它的高可用性。我不想在单个服务器上运行它。
    • 嗯。在这种情况下,您可以拥有允许“主”服务器负责检索和传播更新的逻辑。主服务器将由集群中的服务器“选举”,如果主服务器没有及时响应传播事件,则会进行新的选举。这类似于一些windows网络服务的运作方式
    【解决方案5】:

    有一些您可能知道但未在问题中描述的要求,这使得给出明智的答案具有挑战性。其中一些问题是:

    • 任务必须成功完成吗?
    • 如果任务成功/未成功完成,“谁”需要知道以及需要执行什么类型的操作?
    • 到了再次运行任务时,如果任务还没有完成,会有什么行为?它应该运行还是不运行?
    • 作业以指定的时间间隔运行有多重要?如果间隔是每 5 分钟一次,是否必须每 5 分钟一次,或者任务是否可以在 5 分 10 秒后运行?

    第一步是回答周期性任务将如何安排运行。一个选项是 Windows 计划任务,但它本身并不是高度可用的,但它可能会解决这个问题。如果您使用的是 SQL Server,另一种选择是将 SQL Server 代理用作调度程序,因为它将作为 SQL Server 的一部分进行故障转移。

    下一步要确定的是如何调用 WCF 应用程序。最简单的选择是触发作业以通过 NLB IP 地址调用 WCF 服务。如果数据库服务器(或该区域中的其他服务器)正在调用应用程序区域(当然,总是有例外,例如 MSDTC),这可能被认为是禁止的。

    另一个选择是使用队列模型。在大多数情况下,这将是最可靠的。例如SQL Server 代理可以执行存储过程以在队列表中输入记录。然后在每个应用程序服务器上,服务可以轮询以查找要处理的排队记录。对队列中记录的访问将由数据库序列化,以便第一个服务器运行该作业(并且该作业只运行一次)。

    根据此答案中开放问题的答案,您可能需要添加更多错误处理。如果外部资源的检索通常很短,您可能只想用select for update 锁定队列记录,并在任务完成时更新状态(或删除记录,如果您愿意)。这将阻止其他服务实例在另一台服务器上处理记录时处理该记录,如果在处理期间发生崩溃,则应回滚事务,并且集群中的另一个服务可以获取该记录。 (不过,您可以将事务超时时间增加到您认为需要的时间。)

    如果长时间保持数据库锁定不可行,那么您可以更改逻辑并为服务添加一些监控。现在,当一个作业开始处理时,它的状态将从排队变为运行,并且正在处理记录的服务器将在记录上更新。可以创建某种服务状态表,每个服务实例每次轮询时都会更新当前时间。这将允许集群中的其他服务重新处理显示为正在运行但它们应该在其上运行的服务在特定时间段内没有“签入”的作业。

    这种方法也有局限性:如果任务实际完成但不知何故丢失了数据库连接怎么办——该作业可能会再次运行。当然,我不认为将原子数据库操作与其他非事务性资源(例如 Web 请求、文件系统)结合起来的问题会很容易解决。我假设您正在编写文件或其他内容 - 如果外部内容也放入数据库中,那么单个事务将保证一切都是一致的。

    【讨论】:

    • 我喜欢 SQL Server 代理的建议。我相信很多 RDBMS 都有类似的特性。
    【解决方案6】:

    Zookeeper 是分布式锁的一个很好的用例。 Zookeeper 有 z 节点,它们就像带有数据的目录。

    即使是 netflix 策展人,也有很多食谱已经完成并可以使用。比如:领导选举,分布式锁等等。

    我认为我们有 Zookeeper 的 C# 客户端。您绝对应该尝试此选项。 #选项3

    【讨论】:

      猜你喜欢
      • 2012-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-16
      • 2019-10-09
      • 1970-01-01
      相关资源
      最近更新 更多