本部分说明JGroups构建块接口MessageDispatcher,具体提供一个简单示例来说明如何使用JGroups构建块MessageDispatcher 构建群组通信应用
构建块基于通道之上,是对通道API的更高层抽象,MessageDispatcher提供异步和同步的方法发送消息给集群中的成员并等待响应,我们知道直接使用jGroups API可以向群组发送消息,或从群组接收消息,但发送和接收之间没有任何联系,而使用MessageDispatcher发送消息和接收消息是在同一个事务中完成。本示例演示使用MessageDispatcher 发送消息到集群所有成员并等待响应,验证 GET_ALL 响应模式,超时时间等属性。
本示例集群中有三个成员node1,node2 和node3,node1 为协调者(第一个加入集群)负责集群视图的更新。三个节点都是做相同的事情,向集群中所有节点发送一条消息,并等待接收响应消息。使用JBoss Cluster Framework Demo 介绍所示的方法,任意从SourceForge下载或编译生成DEMO_HOME,本示例的启动脚本msgDispatcher.sh位于DEMO_HOME/bin下。接下来我们依次启动三个节点:
./msgDispatcher.sh -n node1
./msgDispatcher.sh -n node2
./msgDispatcher.sh -n node3
1. node1,node2 和node3中任何一个节点启动后我们都可以看到发送消息到群组的日志信息,如下为node1上输出的日志:
16:36:43,699 INFO [MessageDispatcherTest] Casting message to all group members
Responses: node3-MessageDispatcher Test Message node2-MessageDispatcher Test Message node1-MessageDispatcher Test Message
我们可以看到node3节点打印输出的响应消息有三条,及集群中的所有节点都收到node3发送的消息,且集群中所有节点发送的响应消息都被node3收到。我们可以通过响应模式来控制是否消息发送者要等待所有节点的响应消息,本示例中使用的是 GET_ALL 响应模式,所以只有当接收到所有节点的响应消息或等待超时抛出异常才终止。
3. node1,node2 和node3中任何一个节点启动后的日志中我们可以看到MyRequestHandler处理消息输出的日志,如下为node1上输出的日志:16:37:11,839 INFO [MyRequestHandler] node3, MessageDispatcher Test Message
16:37:11,655 INFO [MyMembershipListener] ViewAccepted, [node1|2] [node1, node2, node3]
[node1|2] [node1, node2, node3]为打印输出的群组视图,node1|2 表示群组的协调者为node1,竖线后面的2表示视图被更新了3次(node1|0,node1|1,node1|2);视图中共有三个成员,即node1,node2 和node3。
本示例所有的源代码可以在cluster/jgroups/stu/src/main/java/.../blocks下找到,接下来我们从代码的层面去解释上面的分析结果,这样有助于更直观的理解构建块MessageDispatcher的理解:
47 channel = new JChannel(props); 48 if(null != name) { 49 channel.setName(name); 50 } 51 handler = new MyRequestHandler(channel); 52 messageListener = new MyMessageListener(); 53 membershipListener = new MyMembershipListener(); 55 disp = new MessageDispatcher(channel, messageListener, membershipListener, handler); 56 channel.connect("MessageDispatcherTestGroup"); 58 Util.sleep(100); 59 logger.info("Casting message to all group members"); 60 Message message = new Message(null, null, new String("MessageDispatcher Test Message")); 61 rsp_list = disp.castMessage(null, message, new RequestOptions().setMode(ResponseMode.GET_ALL).setTimeout(0)); 63 System.out.println("Responses:"); 65 List list = rsp_list.getResults(); 66 for(Object obj : list) { 67 System.out.println(" " + obj); 68 }
9 public class MyRequestHandler implements RequestHandler { ... 20 public Object handle(Message msg) throws Exception { 21 Address sender = msg.getSrc(); 22 logger.info(sender + ", " + msg.getObject()); 23 return channel.getName() + "-" + msg.getObject(); 24 } 25 26 }
如上第9行,MyRequestHandler实现RequestHandler接口,RequestHandler定义了handle方法,用来处理接收到的消息,20-24行为我们实现的handle方法,我们在日志中记录消息发送者的名字和消息的内容(结果分析第3点node1输出的日志“node3, MessageDispatcher Test Message”表示消息发送着是node3,消息的内容是“MessageDispatcher Test Message”),并将自己的名字和接收到消息的内容返回(结果分析第2点node3输出的日志包括“node1-MessageDispatcher Test Message”表示该响应消息是从node1返回的)。
8 public class MyMembershipListener implements MembershipListener { ... 12 public void viewAccepted(View view) { 13 logger.info("ViewAccepted, " + view); 14 }