WebSphere MQ 集群设计和操作

任务:消息: WebSphere MQ 集群设计和操作

集群健康检查

作为 IBM WebSphere MQ 的专业顾问,经常有人打电话叫我过去诊断和解决产品宕机问题,并且常常是紧急的问题。在大部分情况下,不健康的集群是问题的根源。由于具有这方面的经验,我能够识别受损系统中经常出现的问题,并且总结了一些能够避免这些问题并保持集群健康的建议。

当然,WebSphere MQ 集群设计和管理仍然处在探索时期。条条大道通罗马,我见过许多没有按照我建议的某些规则去做的场合,但仍然能够成功地维护集群。但这通常是比较专业化的案例,其中成功主要取决于本地的考虑事项 —— 这意味着存在缓解因素,比如先进的监控或管理技能,或者缺乏复杂的因素,比如系统变更的机会小或集群非常小。

但是随着时间的推移,所有系统都会改变并且本地环境可能让系统经不起改变带来的冲击。因此,我倾向于使用适用性比较广的方法。对于 WebSphere MQ 集群,我在这里建议使用的方法是我认为几乎适用于所有情况的方法。


集群是什么 —— 以及它不是什么

剥离枝节之后,WebSphere MQ 集群本质上仅是一组队列管理器的集合,它们共享相同的名称空间、某些管理功能的代理和密集的 “任意对任意” 连接性。MQ 集群的所有益处都源于这些条件之一或一部分。易于管理是将一部分管理任务委托给内部队列管理器进程的直接结果。拥有一个队列的多个实例的能力是将通用名称空间和密集连接结合的结果。这反过来支持工作负载平衡和动态路由。

但是 WebSphere MQ 集群从来没有超越单个队列管理器的集合的界限,也不试图超越该界限。对于 WebSphere MQ,不存在 “连接到集群” 的概念。连接通常指向集群内部的特定节点。类似地,不管队列管理器是否参与集群,应用程序可用的功能都是一样的。就像点对点消息传递一样,连接到集群队列管理器的应用程序仍然仅能从本地队列获取消息,并且仍然能够处理任何能够在本地解析其名称的队列,不管目标队列位于何处。

这有时候可能令人困惑,因为其他地方也存在术语 “集群”。例如,硬件集群是一个由多个物理服务器组成的逻辑实体。连接到硬件集群的程序能够看到逻辑实体而不是其下的物理组件。类似地,许多应用服务器、数据库和其他软件平台都可以配置为将它们的集合呈现为单个实体。这种用法非常常见,所以对许多人而言,集群这个词意味着由多个物理组件组成的单个逻辑实体。

因此 WebSphere MQ 集群经常被误解是情有可原的。许多人在第一次见到术语 WebSphere MQ 集群时,都不由自主地将其想象成一个由多个物理队列管理器组成的大型虚拟队列管理器,它的用途相当于一个队列管理器。类似地,术语集群队列通常让人联想到用作单个逻辑队列的多个队列实例,这个逻辑队列可以进行连接并发送和获取消息。但这种对 WebSphere MQ 的工作方式的主观判断是错误的,因此误解可能导致糟糕的设计或操作选择。

WebSphere MQ 集群不关注应用程序如何与队列管理器交互,它关注队列管理器之间如何交互。


WebSphere MQ 集群的好处

当 WebSphere MQ 引入集群的概念时,新词点对点网络被用于区分集群和一般的 MQ 网络拓扑。它们之间的主要区别是在点对点网络中,队列消息从原点到达一个目标点。在这种网络中路由线路是根据每个队列决定的,并且在构建时被嵌入到网络定义中。

相比之下,集群中的消息从原点到达多个可能的目标点。对于这种情况,对每条消息的目标点和路由的选择都发生在运行时。结果是集群 WebSphere MQ 网络是灵活的、动态的和有弹性的。

这些特性让 WebSphere MQ 集群非常适用于面向服务架构(SOA)。但是,更重要的是 WebSphere MQ 集群没有取代点对点网络。当需要点对点接口时(例如,传统的批处理接口),可以在 WebSphere MQ 集群中轻松实现它。这种让点对点和集群拓扑共存的能力为平稳过渡到 SOA 实现铺平了道路。

不过,集群必须健康才能充分发挥其功用。本文的其余部分主要介绍我这几年来总结的一些方法,用于帮助您构建健康的集群并维持集群的健康状态。


针对储存库的建议

在提供建议之前,我先提供一些想法来让我们统一一下概念。

任何集群队列管理器都可以对集群公开一部分队列或主题,而其余的队列则是本地的。对于向集群公开的对象,必须跟踪某些状态信息才能让集群正确的路由信息。除了队列和主题之外,客户还跟踪集群中管道和队列的状态。所有这些集群对象的名称和状态都是集群元数据。

集群中的两个队列管理器被指定为维护集群中的所有元数据副本是完整的、最新的。在健康的集群中,每个这些节点都包含与集群的状态相关的所有信息的镜像副本。因为它们具有完整的数据集,所以这些节点称为完整储存库

其他所有集群成员都维护集群元数据的子集,因此被称为部分储存库。每个这些节点都维护它储存的每个集群对象的实时状态。对这些集群对象的任何更改都会立即发布到两个完整储存库中。此外,连接到部分储存库队列管理器的应用程序需要将消息放到集群队列中,集群队列可能驻留在其他节点上。部分储存库订阅关于这些远程对象的更新,并维护每个对象的最新状态的本地副本。

术语完整储存库和部分储存库可能有些复杂甚至有些让人困惑。在常见的用法中,“完整储存库” 通常简称为 “储存库”,而所有其他节点都是参与集群的队列管理器。这与使用 REPOS 作为参数的 WebSphere MQ 命令集是一致的。

例如,您可以发出 REFRESH CLUSTER(*) REPOS(YES) 或 ALTER QMGR REPOS(集群名)。这是我在本文的其余部分遵循的约定。但我提到 “储存库” 时,我指的是指定为完整储存库的两个队列管理器之一。其他东西仅是指集群成员。

以下是我的建议:

  • 最佳的数量为二

    我在前面提到指定为储存库的队列管理器为两个。这不是强制的,但我强烈推荐使用两个。集群使用一个储存库就能正常工作,甚至短期内不使用储存库也能勉强维持。但是两个储存库提供更高的可用性。最后,必须对储存库进行维护,使用两个储存库时,当一个储存库离线时其他一个仍然能够保证集群正常工作。

    如果两个很好了,三个是不是更好?不一定。当集群的对象的状态发生变化时,每个集群成员将发布两个更新。如果仅有一个储存库,它需要接收两个更新。如果有两个储存库,每个储存库接收一个更新。对于这两种情况,所有集群节点都将更改报告给所有集群,并且这种拓扑为跨集群一致地维护状态提供内置保证。

    对于存在三个以上储存库的情况,每个集群成员仅更新两个储存库。跨储存库维护一致性取决于它们能否相互成功更新。使用两个储存库时,100% 的元数据直接来自集群成员。使用三个储存库时,1/3 的元数据通过复制提交。使用 4 个储存库时,这个数值为 1/2。使用 5 个时上升到 3/5。存在的副本越多,副本出现错误的机会就越大。

    对于储存库,数量太多也不是一件好事。我的所有集群设计都严格使用两个储存库,不多也不少。这个规则也存在少数例外。例如,对于跨国集群,我有时使用本地储存库对来克服延迟。但这并不意味着使用 4 个储存库就很好,而是比运行两个储存库时延迟有所改善。

  • 位置

    设计应用程序的一个常见实践是以生产标准布局拓扑,让灾难恢复站点成为副本。这在术语中通常称为 active/passive。尽管这在故障转移非常迅速的点对点结构中非常有效,但在 SOA 环境中服务是广泛分布的并且需要能够单独进行故障转移。这就是 active/passive 模式变得很常用的原因。尽管 WebSphere MQ 集群是基础设施,它实际上也是一个 SOA 应用程序。集群成员从储存库请求访问,后者则反过来提供运行时查询和状态管理。如果我们按照传统的应用程序实现集群,那么生产中将有两个活动储存库,而灾难恢复站点中有两个冷备份储存库。这种方法的问题在于灾难恢复站点需要储存集群的当前状态,以在故障转移期间立即可用。当故障转移包含整个生产环境的部分信息时,尤其是这样。

    在生产环境中运行一对活动储存库并在灾难恢复中运行另一对活动储存库能够解决并发性问题,但是它将把我们带回到复制问题。解决这两个问题的办法是在生产环境中运行一个活动储存库,在灾难恢复站点中运行另一个活动储存库。

  • 专用服务器是最好的

    这是我最倾向于强调的建议,因为我经常为储存库指定专用的服务器,即使对比较小的集群也是一样。如果我可以选择的话,我宁愿将储存库放置在多余的低端、独立服务器上,而不将它与其他应用程序一起放置在高可用的昂贵硬件上。在这点上,我与主流观点是相反的,我将进一步讲解。

    如果我必须将储存库与应用程序队列管理器放置在同一个服务器上,那么我将选择独立的、专用的队列管理器。因此,以下用例的效果从上至下依次变差:

    • 储存库驻留在专用服务器上。
    • 储存库驻留在专用的队列管理器上,但与一个或多个应用程序队列管理器共享一个节点。
    • 将储存库与应用程序队列管理器放在一起。

    对于专用服务器规则存在一个例外,即集群非常小,删除它能够解决问题并且不给应用程序带来很大影响。如果我有 4 至 6 个应用程序队列管理器并且其中两个位于储存库中,那么我通常维护一组能够拆分集群并快速重构集群的脚本。这仍然需要在集群范围内停机,但停机的时间很短。问题是这样做的伸缩性不强。再添加一些队列管理器将导致难以在拆分集群之后快速从头构建可靠的集群。由于集群有随着时间而增长的趋势,因此我把这看作一种妥协和临时解决办法。

    将储存库与应用程序队列管理器放置在一起的问题是它在多个级别创建依赖项。其中一个依赖项是底层主机上的软件版本。当更新到新的 WebSphere MQ 版本时,我们推荐首先将储存库升级到最新的版本。尽管没有严格要求,但是在混合集群中使用低级的储存库不利于使用高级特性。对于最坏的情况,这将导致集群宕机。但将储存库与应用程序队列管理器放置在一起时,升级的能力取决于应用程序团队可用于测试和验证新版本的资源。即使存在一个依赖应用程序,也会出现这个问题,但几个应用程序共享相同的队列管理器是很常见的。

    不过,在这里升级是最佳的选择。最糟糕的情况是出现需要应用补丁或修复包的集群问题。我的一位客户曾经遇到过这样的问题,集群不能正常工作并且每晚都发生宕机,这源于他们没有正确应用修复包。如果在将储存库驻留在应用程序队列管理器之前早五年做出决策是比较好的。经常宕机导致的损失已经抵得上使用专用服务器、购买许可和购买 10 年驻留服务的成本。

    但是这些依赖项甚至会延伸到 WebSphere MQ 管理界面,因此我不能获得专用主机时,我将使用专用的队列管理器。用于修复储存库的命令是 RESET。修复集群成员队列管理器的命令为 REFRESH。这两个命令是相互排斥的,即您不能在完整储存库上运行 REFRESH CLUSTER(*) REPOS(YES)。为了在储存库上运行 REFRESH 命令,首先需要将它降级为非储存库。在这里通常需要进入其他储存库中并通过 RESET 命令将旧储存库从集群移除。当在旧储存库上运行 REFRESH 命令时,它将 “忘记” 它对集群的了解,并作为常规的集群成员重新加入集群。

    使用 RESET 和 REFRESH 命令之后,集群中的所有其他节点将丢失它们曾经了解的关于修复的储存库的信息。这可能是一个问题,不是因为储存库没有驻留任何应用程序对象,而是因为它对集群中的应用程序产生影响。类似地,本地应用程序在 REFRESH 期间将丢失对集群的其余成员的了解。再次将储存库提升为节点将恢复所有集群队列的信息,但在此之前,应用程序可能遇到错误,因为它们使用的队列突然不能解析。

    在操作级别上也存在依赖项。集群中的消息直接从发送节点提交到接收节点。在健康的集群中,不存在消息经过一个或多个节点才达到目的地的情况。因此,在专用的队列管理器上驻留储存库导致这样一个拓扑结构,其中应用程序数据和集群数据从不经过相同的管道。每个应用程序可能生成大量对其他应用程序产生不良影响的消息,这取决于集群的大小和应用程序的本质。使用专用储存库队列管理器减少了这些交互带来的不稳定性。

    当然,当应用程序和集群储存库争用有限的资源时,还存在许多其他交互。尽管集群储存库是一个本机 MQ 进程,但是它仍然和其他应用程序一样受到资源限制的影响。例如,使用许多管道连接的应用程序可能让储存库进程达到 MAXCHANNELS 限制。由于急切需要进行连接,它将不能够快速发布集群更新。类似地,储存库可能与应用程序争用同步事务、事务日志空间、集群传输队列的 MAXDEPTH 和许多其他资源。对于所有这些情况,驻留在相同的队列管理器上的集群和应用程序是直接争用资源的。同样,当储存库和应用程序队列管理器共享相同的服务器时,可能导致服务器级别的争用,比如磁盘空间、内存、CPU 和其他系统资源的争用。

    我们的经验是将储存库放在 WebSphere MQ 集群中最可靠、可用性最高的硬件上。尽管我喜欢使用高级的服务器,但我总是把独立的储存库看得比高级的硬件更重要。当您要对许多不同的应用程序协调修复包时,硬件的可靠性一般是不用担心的。


针对集群成员的建议

  • 仅使用一个显式定义的 CLUSSDR

    尽管集群中有两个储存库,但没有必要都为它们显式地定义用于加入集群的集群发送管道。当已定义第一个 CLUSSDR 管道并且队列管理器成功地加入集群中时,将立即通知它其他储存库的存在和构建与它通信的管道。没有必要显式地定义第二个 CLUSSDR。

    如果第二个 CLUSSDR 管道没有必要,那么就要考虑定义它是否有负面影响了。

    集群成员通常向两个储存库发布更改。向哪两个储存库发送部分取决于已定义的 CLUSSDR 管道。因为显式地定义的 CLUSSDR 更加方便,所有发布通常指向它们,而不是自动定义的管道。如果显式地定义了两个 CLUSSDR 管道,发布通常使用这两个管道,而不考虑管道另一端的队列管理器是否可用,也不考虑其他可选目标是否可用。

    在常规的操作过程中,这不一定是件坏事。但如果您需要驻留一个新的储存库,不管是临时还是作为永久迁移,显式地定义两个 CLUSSDR 管道将给您带来不断的麻烦。要让集群成员看到新的储存库的惟一方法是删除其中一个显式的 CLUSSDR 定义,通常还可以一起使用 REFRESH CLUSTER(集群名)REPOS(YES) 命令。

    使用两个以上显式定义的 CLUSSDR 管道的结果更加糟糕。对于这种情况,就不能确定需要向哪两个管道发布更改,如果两个选择的储存库都离线,集群成员的发布将停留在集群发送队列中。

    最可靠的方法是使用一个显式定义的 CLUSSDR。这让队列管理器能够使用常规的工作负载管理算法找到第二个储存库。例如,您可以让第三个储存库上线,然后在执行维护之前暂停主储存库之一。集群成员将向它们的显式 CLUSSDR 指向的储存库发布一次 —— 不管它是否在线 —— 然后向剩余的两个储存库之一发布一次,这取决于哪个储存库是可用的。


针对所有节点的建议

  • 每个集群使用惟一的 CLUSRCVR 名

    产品手册演示了使用 TO.<QMgr name> 等管道名构建集群。例如,名为 QM1 的队列管理器将创建一个名为 TO.QM1 的集群接收器管道,其他的所有集群成员将使用这个管道连接到它。这里需要担心的问题是命名约定会导致相同的管道在多个重叠的集群之间被重用,这将造成问题。

    要理解为什么这会造成问题,您首先需要理解为什么要使用重叠的集群。这有许多原因,但最常见的原因是它们提供对集群的某些方面的粒度管理。但是集群管理操作不可避免地用到 CLUSRCVR。如果您 REFRRESH 集群,必须对它参与的所有集群停止 CLUSRCVR。因为命名约定鼓励您跨重叠集群重用集群管道,因此这就剥夺了重叠集群应该提供的功能。

    我尝试使用的命名约定是 <Cluster name>.<QMgr name>,因为它要求每个集群使用的管道是惟一的。使用这种命名约定,就可以在其中一个集群上执行维护,而不影响到队列管理器参与的其他集群。

  • 限制管理访问的 MCAUSER

    任何带有空白 MCAUSER 值的入站管道将允许任何连接到的队列管理器管理本地队列管理器。对于 CLUSRCVR 管道,空白的 MCAUSER 意味着集群中的任何其他队列管理器都可以管理本地队列管理器。这通常不是您想要的,因为合法的管理员通常从底层主机的命令行直接连接或管理队列管理器。

    将 CLUSRCVR 管道的 MCAUSER 设置为一个服务帐户将强制它使用该帐户的特权。您可以使用 setmqaut 指定管道可以将消息放到哪个队列上。使用简单的配置能够访问几乎所有队列,但 SYSTEM.ADMIN.COMMAND.QUEUE、初始化队列和大部分 SYSTEM.* 队列除外。但更加安全的做法是拒绝访问非授权队列,这些队列仅能由少数有特权的人访问。

    不管是哪种情况,管道都必须能够在 SYSTEM.CLUSTER.COMMAND.QUEUE 上放置消息,以让队列管理器成为集群的成员。

    当然,为了让这起到作用,必须对这些入站管道进行保护,避免受到管理员的干扰。这里的 “入站管道” 指的是每个 RCVR、RQSTR/CLUSRCVR 或 SVRCONN 管道,包括名为 SYSTEM.DEF.* 和 SYSTEM.AUTO.* 的管道,它们甚至位于禁用自动定义管道的队列管理器上。


结束语

关于创建集群有许多需要学习的地方,本文仅讨论了一小部分我认为比较重要的内容。我在这里提供的建议大部分都与策略或流程变更相关。其中一条建议需要资金的支持,即将储存库驻留在专用的服务器上,但我经常受到挑战,尤其是当集群仅包含少量节点时。

最常被提及的一个问题是 “我能否等到集群变大时才购买新的节点?”您当然可以这样做。不过,如果能够尽快实现目标拓扑 —— 尽管当前集群仍然很小 —— 那么实现它有很多好处,其中之一就是在需要升级时,不再需要考虑使用什么标准。除非存在影响,否则任何决策都带有一些任意性。因此,在很多情况下决策都推迟了,直至出现问题并且不得不面对时才做出决策。当我听到这个问题时,我理解为 “我能否在出现实质影响时才做出决策?”

我不能保证您的集群永远不出现问题,但如果您采用了这些建议,我相信您遇到的问题会大大减少,并且解决问题也要容易得多。

<!--CMA ID: 477253--><!--Site ID: 10--><!--XSLT stylesheet used to transform this file: dw-document-html-7.0.xsl-->

相关推荐