1. Websphere MQ 集群负载均衡的增强
在Websphere MQ集群中,成员队列治理器可以创建本地队列,并将其在集群中共享,集群中的其他成员队列治理器不需要任何额外的配置操作,就可以像访问本地队列一样向该队列放入(PUT)消息;每个成员队列治理器都可以创建与之同名的共享队列,于是该共享队列在集群中就拥有了多个副本,所有的副本将作为一个虚拟的整体;对访问者(调用MQPUT的应用)而言,它们就是一个队列,应用不需要关心如下细节:
所有这些细节问题由集群负责处理,由此实现了集群的负载均衡功能。
然而,在V6之前,集群的负载均衡功能在实现上有若干的限制,其中我们最常碰到的是如下两点:
即便Websphere MQ具有上述的局限,但瑕不掩瑜,在大规模集群方面,消息中间件(MOM)业内还是少有能出其右者。这也许是在消息中间件技术不算悠久的发展历程上,无法回避的成长的阵痛。究竟,MQ还是为我们提供了解决问题的途径,虽然牺牲了些许的系统部署、治理的复杂度,手法也显的不是那么的优雅。
"是否优先使用本地副本?是否采用轮循的分配策略?",在这个问题上,使用者应当具有自由的裁量权利,并拥有便捷的设置手段。我们终于在Websphere MQ v6里找回了那些被剥夺了的选择的自由。
在Websphere MQ v6中,使用者可以根据集群成员节点的实际处理能力,合理、灵活的分配工作负载,轮循(Round Robin)顺理成章的成为缺省的分发策略。本地共享队列副本优先的策略也依然是缺省的设置,但用户可以非常方便的修改策略,使集群中的其它共享队列副本可以和本地副本一样参与到消息分发的机制当中。当然,用户出口(User Exit)程序仍然被支持,用户可以使用该机制实现更加个性化的负载均衡策略。②
为支持上述新增功能,Websphere MQ为队列、通道、队列治理器等对象增加了若干的参数。
对象参数命解释缺省值队列(Queues)CLWLUSEQ指定该队列在负载均衡策略中,本地队列副本参与分发的方式。
2. 演示
下面,我们来看两个简单的演示场景:
在集群CL1中有三个成员队列治理器QM1、QM2、QM3 , 每个成员上都拥有一个共享队列Q1的副本。另有一个队列治理器QM4, QM4和集群CL1中的QM1之间具有收发通道。
首先,依照上图配置集群的基本环境(具体的配置方法可以参考Websphere MQ产品文档《Queue Manager Clusters》和IBM开发者园地中娄丽军的文章《MQ群集的使用》)。
2.1 场景一
第一个场景中,应用程序将连接到队列治理器QM4, 发送大量消息到集群CL1中的共享队列Q1中。我们期望消息以 1:3:6的比例在QM1、QM2、QM3中分配。
首先,由于集群的接入点QM1要参与消息的分配,QM1要拥有一个共享队列Q1的副本,而根据Websphere MQ缺省分发策略(本地优先),这会造成所有消息都保存到QM1上的Q1中,因此我们必须修改变Q1的分发策略;有两种方式可以达成这个目的:
1. 修改QM1上的队列Q1的属性CLWLUSEQ为ANY,(缺省为QMGR)。
2. 修改QM1上的队列Q1的属性CLWLUSEQ为QMGR,修改队列治理器QM1的属性CLWLUSEQ为ANY。第一种方式只对Q1生效,而第二种方式则可能对队列治理器中的所有队列生效;假如集群中有大量的共享队列,并采用同样的分发策略,后一种方式会更灵活些。
runmqsc QM1
ALTER QLOCAL(Q1) CLWLUSEQ(LOCAL)
接下来,为了使发送来的消息可以被动态路由,需要在接入点队列治理器QM1上创建一个空的队列治理器别名(Queue-manager alias)③。
DEFINE QREMOTE(ANYONE) RNAME(' ') RQMNAME(' ')
队列治理器QM4置身于集群之外,在其上定义远程队列定义(假设通道、传输队列都已经就绪了):
DEFINE QREMOTE(Q1) RNAME(Q1) RQMNAME(ANYONE) XMITQ(QM1)
调用MQ例子程序amqsput,发送10个消息:
amqsput Q1 QM4
Sample AMQSPUT0 start
target queue is Q1
1
2
3
4
5
6
7
8
9
0
我们看一下这样设置的效果:
runmqsc QM1
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(3)
runmqsc QM2
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(3)
runmqsc QM3
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(4)
可以看到,各副本中的消息数分别是3、3、4。基本上被平均分配到集群中的三个成员上了。
接下来,我们设置各共享队列副本的权重;确切的讲,权重只能在通道上设置,我们要修改每个集群成员队列治理器的集群接收通道(Cluster-receiver Channel)的属性CLWLWGHT。
设置QM1的权重为1。
runmqsc QM1
ALTER CHANNEL(TO.QM1) CHLTYPE(CLUSRCVR)CLWLWGHT(1)
设置QM2的权重为3。
runmqsc QM2
ALTER CHANNEL(TO.QM1) CHLTYPE(CLUSRCVR)CLWLWGHT(3)
设置QM3的权重为6。
runmqsc QM3
ALTER CHANNEL(TO.QM1) CHLTYPE(CLUSRCVR)CLWLWGHT(6)
清空各队列中的消息后,再次发送消息。
amqsput Q1 QM4
Sample AMQSPUT0 start
target queue is Q1
1
2
3
4
5
6
7
8
9
0
看一下效果吧:
runmqsc QM1
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(1)
runmqsc QM2
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(3)
runmqsc QM3
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(6)
1、3、6 ,Bingo!
2.2 场景二
第二个场景,我们将看到所谓Hot-Standby模式:在正常状况下,消息有确定的分发目的地(一个或多个共享队列副本),当首选的目的地失效时,消息将发送到备份的(一个或多个)副本中。
这次,我们期望队列治理器QM2和QM3上的Q1副本作为正常状况下的消息目的地。在上述两个队列副本不可用时,由QM1上的Q1副本作为备份接收消息。
同样有两种设置方式:
1. 修改各成员队列治理器上的共享队列副本Q1的属性CLWLPRTY;
2. 修改各成员队列治理器上的集群接收通道的属性CLWLPRTY。
第一种方式只对Q1生效,而第二种方式则可能对相应队列治理器中的所有队列生效;假如集群中有大量的共享队列,并采用同样的分发策略,后一种方式会更灵活些。
假如两种同时设置,则集群接收通道的属性CLWLPRTY优先生效。
设置QM1上的Q1副本优先级为1。
runmqsc QM1
ALTER QLOCAL(Q1) CLWLPRTY (1)
设置QM2上的Q1副本优先级为2。
runmqsc QM2
ALTER QLOCAL(Q1) CLWLPRTY (2)
设置QM3上的Q1副本优先级为2。
runmqsc QM3
ALTER QLOCAL(Q1) CLWLPRTY (2)
清空各队列中的消息后,再次发送消息(这次是9个消息)。
amqsput Q1 QM4
Sample AMQSPUT0 start
target queue is Q1
1
2
3
4
5
6
7
8
9
runmqsc QM1
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(0)
runmqsc QM2
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(3)
runmqsc QM3
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(6)
队列治理器QM2、QM3上的Q1优先级同为2,高于QM1上的Q1,所以消息在QM2、QM3中分配,分配的比例则依照之前我们设置的通道的权重3:6。
下面我们将QM3上的Q1设置为不可写入的状态,并观察一下结果。
runmqsc QM3
ALTER QLOCAL(Q1) PUT(DISABLED)
清空各队列中的消息后,再次发送消息。
amqsput Q1 QM4
Sample AMQSPUT0 start
target queue is Q1
1
2
3
4
5
6
7
8
9
0
我们得到如下的结果:
runmqsc QM1
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(0)
runmqsc QM2
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(10)
runmqsc QM3
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(0)
在QM3上的Q1不可用时,所有的消息都被发送到QM2上的Q1中。
我们再将QM2上的Q1也设置为不可写入的状态,并观察结果。
runmqsc QM2
ALTER QLOCAL(Q1) PUT(DISABLED)
清空各队列中的消息后,再次发送消息。
amqsput Q1 QM4
Sample AMQSPUT0 start
target queue is Q1
1
2
3
4
5
6
7
8
9
0
我们得到如下的结果:
runmqsc QM1
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(0)
runmqsc QM2
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(0)
runmqsc QM3
display qlocal(Q1) CURDEPTH
AMQ8409: Display Queue details.
QUEUE(Q1) TYPE(QLOCAL)
CURDEPTH(10)
当高优先级的Q1副本都不可用时,所有的消息都被发送到优先级较低的QM1上的Q1中。
2.3 更多
另一个我们没有深入讨论的概念是队列和通道的属性CLWLRANK(我们姑且称之为"等级"好了)。CLWLRANK和CLWLPRTY作用相似,不同之处在于CLWLRANK在通道状态检查之前生效,而CLWLPRTY在通道状态检查之后生效。简单的讲,假如设置了CLWLRANK,则消息将必定发往 CLWLRANK最高的队列副本,即使通向该目标副本的通道不可用,消息也将积压在集群的传输队列中,等待通道恢复,而不会发送到目前可用的其它 CLWLRANK较低的副本中。假如设置了CLWLPRTY,则集群会首先检查通向各副本所在地的通道状态,只有通道可用的目的地才会进入候选名单,集群将在候选名单中选择CLWLPRTY最高的副本来分发消息。
最后,我们总结一下各参数生效的先后次序:
我们初步体会了Websphere MQ新的集群负载均衡功能概貌,当然,这还远远不足以体现它全部的内涵。假如将所有这些属性加以灵活的组合,将衍生出为数众多的负载均衡配置方案,来满足实际业务环境多样化的需求。还有相当多与负载均衡相关的内容,本文没有涉及到,但非常值得探讨,希望本文能引起诸位对MQ集群的爱好,并参加到我们的讨论之中。
新闻热点
疑难解答