WebLogic JMS的强大功能组合:UOW+SAF+分布式Queue

来源:互联网 发布:淘宝上买徕卡 编辑:程序博客网 时间:2024/05/17 01:57
 

Weblogic 服务器提供了非常强大的JMS消息中间件平台,其性能优越,远超同类JMS产品比IBM 的MQ也快很多。并且,Weblogic JMS提供了非常强大的消息传递功能使得基于该平台的Java 消息解决方案非常的完整。本文,旨在阐述WebLogic JMS 服务器的一些比较重要的特性,以及如何配合在一起使用。

 

 

  1. 本文所要介绍的WebLogic JMS特性有:

DQ (Distributed Queue):分布式Queue。通过配置DQ可以提供消息队列的集群式部署,提高一个队列的Capacity以及提供HA支持。对于客户端透明。

SAF 存储转发:可以在两个WebLogic服务器(相同版本)之间,从一段发送JMS消息到另一端,并保证发送能到达终端的Destination。如果,两台服务器之间的网络连接暂不可用,则会将消息保存至发送方的本地存储中。用户可以设定多种转发策略和Qos设定。

UOW(Unit-Of-Work):WLS可以将多条消息标签为一个工作单元(UOW),只有当该UOW中所有的消息都被发送到队列中后,Consumer端才能获取该工作单元的所有消息。并且,所有属于该UOW的消息,保证只有一个Consumer能进行处理。

 

 

  1. 客户用例

在某家工厂构建的一个门店和总部数据中心系统之间的一个异步信息同步的解决方案中,采用了UOW+SAF+DQ的方式来传送大文件的切片,并在终端还原文件。由于文件被切片传送,因此,根据JMS 队列传输的原理,默认配置的JMS DQ会将文件切片分散接收在集群的各个节点的QUEUE上,并且每个节点上配置的消息驱动EJB(MDB)也不能保证只有一个实例能获取文件的全部切片,这样的默认行为令还原大文件切片变为一个需要在多个MDB之间协调完成的事务,处理过程复杂,效率也不高。并且通过SAF来克服在VPN网络环境中可能的各类异常,保证消息传递的质量。

 

       针对该用户的这种需求,提出的解决方案是使用UOW来将一个文件的切片打包为一个工作单元的方式来传送。

方案示意图如下:

 

上图中的UOW1、UOW2、UOW3表示的是代表一个大文件切片的一组/一个工作单元消息(多条)。大文件会先经过JAVA程序进行切片,然后通过JMS API和UOW的API来发送到一个本地的WebLogic服务器的队列上。本地队列被配置为通过SAF转发到远程的一个DQ中。远程的DQ接收到消息后,会根据UOW的ID来将一组消息(统一UOW ID的消息)作为一个整体,分配到集群中的某一个实例。在该集群中部署一个MDB EJB。该EJB的实例会收到属于一个文件的所有切片,然后进行还原处理。由于使用了UOW,可以保证每个文件的所有切片,有且只有一个MDB实例可以进行处理。

 

  1. 配置过程简述

 

  1. 首先安装远端WebLogic服务器并配置好集群。
  2. 分别创建三个JMS Server在三个集群节点上。
  3. 创建一个JMS Module,并在JMS Module中定义一个子部署target到cluster上。
  4. 创建一个Distributed Queue, target到定义好的子部署上,记得在高级配置区域中选择工作单元 (UOW) 消息处理策略为单个消息传递。 该配置启动UOW支持。
  5. 安装本地WebLogic服务器
  6. 创建SAF存储转发代理。
  7. 创建SAF导入目的地,并在SAF远程上下文定义中,配置远程集群的URL,例如:t3://172.16.100.1:7003,172.16.100.1:7004,172.16.100.1:7005。
  8. 配置子部署并将SAF导入目的地target到子部署上。

详细部署请参考WLS相关文档。

 

  1. 示例代码

UOW 发送:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import java.util.UUID;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Hashtable;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import weblogic.jms.extensions.WLMessageProducer;
  
/** This example shows how to establish a connection
 * and send messages to the JMS queue. The classes in this
 * package operate on the same JMS queue. Run the classes together to
 * witness messages being sent and received, and to browse the queue
 * for messages. The class is used to send messages to the queue.
 */
public class QueueSendUOW
{
  // Defines the JNDI context factory.
  publicfinal static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory";
  
  // Defines the JMS context factory.
  publicfinal static String JMS_FACTORY="dizzyworldConnectionFactory";
  
  // Defines the queue.
  publicfinal static String QUEUE="dizzyworldDistributedQueue";
  
  privateQueueConnectionFactory qconFactory;
  privateQueueConnection qcon;
  privateQueueSession qsession;
  privateQueueSender qsender;
  privateQueue queue;
  privateTextMessage msg;
  
  /**
   * Creates all the necessary objects for sending
   * messages to a JMS queue.
   *
   * @param ctx JNDI initial context
   * @param queueName name of queue
   * @exception NamingException if operation cannot be performed
   * @exception JMSException if JMS fails to initialize due to internal error
   */
  publicvoid init(Context ctx, String queueName)
    throwsNamingException, JMSException
  {
    qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
    qcon = qconFactory.createQueueConnection();
    qsession = qcon.createQueueSession(true, Session.AUTO_ACKNOWLEDGE);
    queue = (Queue) ctx.lookup(queueName);
    qsender = qsession.createSender(queue);
  
    msg = qsession.createTextMessage();
    qcon.start();
  }
  
  /**
   * Sends a message to a JMS queue.
   *
   * @param message  message to be sent
   * @exception JMSException if JMS fails to send message due to internal error
   */
  publicvoid send(String message, String strUOW,int SeqNo, boolean isLast) throws JMSException {
    msg.setText(message);
    msg.setStringProperty("JMS_BEA_UnitOfWork", strUOW);
    msg.setIntProperty("JMS_BEA_UnitOfWorkSequenceNumber", SeqNo);
    msg.setBooleanProperty("JMS_BEA_IsUnitOfWorkEnd", isLast);
    qsender.send(msg, DeliveryMode.NON_PERSISTENT,7, 0);
  }
  
  
  /**
   * Closes JMS objects.
   * @exception JMSException if JMS fails to close objects due to internal error
   */
  publicvoid close() throwsJMSException {
    qsession.commit();
    qsender.close();
    qsession.close();
    qcon.close();
  }
  
  
 /** main() method.
  *
  * @param args WebLogic Server URL
  * @exception Exception if operation fails
  */
  publicstatic void main(String[] args) throws Exception {
    if(args.length != 1) {
      System.out.println("Usage: java QueueSendUOW WebLogicURL");
      return;
    }
    InitialContext ic = getInitialContext(args[0]);
    QueueSendUOW qs =new QueueSendUOW();
    qs.init(ic, QUEUE);
    readAndSend(qs);  
    qs.close();
  }
  
  
  privatestatic void readAndSend(QueueSendUOW qs)
    throwsIOException, JMSException
  {
    BufferedReader msgStream =new BufferedReader(newInputStreamReader(System.in));
    String line=null;
    UUID UOW =null;
    booleanquitNow = false;
      
    intseqNumber = 1;
  
    UOW = UUID.randomUUID();
    while(! quitNow) {
      System.out.print("Enter message (\"quit\" to quit): \n");
      line = msgStream.readLine();
      if(line != null && line.trim().length() != 0) {
        quitNow = line.equalsIgnoreCase("quit");
        if( ! quitNow ) {
          qs.send( line, String.valueOf(UOW), seqNumber,false );
        }else {
          qs.send( line, String.valueOf(UOW), seqNumber,true );
        }
        System.out.println("JMS Message Sent: "+ line + "\n");
        seqNumber +=1;
      }
    }
  }
  
  
  privatestatic InitialContext getInitialContext(String url)
    throwsNamingException
  {
    Hashtable<String,String> env =new Hashtable<String,String>();
    env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
    env.put(Context.PROVIDER_URL, url);
    returnnew InitialContext(env);
  }
  
}

 

MDB 接收:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package mdb;
  
   
  
import java.util.ArrayList;
  
   
  
import javax.ejb.MessageDriven;
  
   
  
import javax.ejb.MessageDrivenContext;
  
   
  
import javax.jms.JMSException;
  
import javax.jms.Message;
  
import javax.jms.MessageListener;
  
import javax.jms.ObjectMessage;
  
import javax.jms.TextMessage;
  
   
  
@MessageDriven(mappedName ="dizzyworldDistributedQueue")
  
public class MessageDrivenEJBBean implementsMessageListener {
  
    privatestatic final boolean VERBOSE = true;
  
    privateint count =0;
  
    privateMessageDrivenContext m_context;
  
    privateint m_tradeLimit;
  
    publicvoid onMessage(Message msg) {
  
         
  
        try{
  
            System.out.println(count++);
  
            System.out.println(msg.getClass());
  
            if(msg instanceofObjectMessage) {
  
                ArrayList msgList =
  
                    (ArrayList)(((ObjectMessage)msg).getObject());
  
                intnumMsgs = msgList.size();
  
                log("Received ["+ numMsgs + "] Messages");
  
                log("UOW id: "+
  
                                   (((TextMessage)msgList.get(0)).getStringProperty("JMS_BEA_UnitOfWork")));
  
                for(int i = 0; i < numMsgs; i++) {
  
                    log("Message["+ i + "] " +
  
                                       ((TextMessage)msgList.get(i)).getText());
  
                }
  
                System.out.println();
  
            }
  
        }catch (JMSException jmse) {
  
            // TODO: Add catch code
  
            jmse.printStackTrace();
  
        }
  
    }
  
   
  
   
  
    privatevoid log(String s) {
  
        if(VERBOSE)
  
            System.out.println(s);
  
    }
  
}

 5. 测试

设定相关类路径并启动发送客户端,示例:java – cp %CLASSPATH% QueueSendUOW t3://172.16.100.102:7001

启动后输入一条或多条消息,输入’quit’完成一个UOW。

 

将MDB部署到集群中,在WLS的输出信息中,检查消息接收。

 

原创粉丝点击