最近工作需要,做了与泛微OA系统的MQ消息集成,现在与大家分享,共同学习,有不足处请提出,尽量优化提高。
一、ActiveMQ简介
ActiveMQ是Apache软件基金下的一个开源软件,它遵循JMS1.1规范(Java Message Service),是消息驱动中间件软件(MOM)。它为企业消息传递提供高可用,出色性能,可扩展,稳定和安全保障。ActiveMQ使用Apache 许可协议。因此,任何人都可以使用和修改它而不必反馈任何改变。这对于商业上将ActiveMQ用在重要用途的人尤为关键。MOM的工作是在分布式的各应 用之间调度事件和消息,使之到达指定的接收者。所以高可用,高性能,高可扩展性尤为关键。
二、实战
以下描述性的语言较少,只是将主要代码贴出来,有部分注释。
1、客户端连接MQ服务器(MQConnection.java)
public class MQConnection { // 日志对象初始化 private static final Logger logger = LoggerFactory.getLogger(MQConnection.class); // MQ服务器服务地址(failover:initialReconnectDelay:第一次连接时延迟毫秒数, // maxReconnectDelay:每次重连最大间隔毫秒数) // 建议在具体应用中调整为使用配置参数设置此默认值 // private static final String DEFAULT_URL = "failover:(tcp://10.199.81.33:61616)"; private static final String DEFAULT_URL = MQStateAttribute.CONNURL; // 可为每个对象单独设置自己的Connection url private String url = DEFAULT_URL; // 是否对每个对象都使用默认的Connection对象 private boolean useDefaultConn = true; // JMS链接对象 private Connection conn; // 统一的Connection对象 private static Connection defaultConn; // 获得Connection public Connection getConn() throws Exception{ if (useDefaultConn) { // 如果使用统一Connection对象且该对象未创建过,则进行创建并返回 // 使用双层判断保证永远不可能创建一个以上的Connection对象 if (defaultConn == null) { synchronized (MQConnection.class) { if (defaultConn == null) { defaultConn = createConn(DEFAULT_URL); } } } return defaultConn; } else { // 如果不使用统一Connection对象且该对象未创建过,则进行创建并返回 // 使用双层判断保证永远不可能创建一个以上的Connection对象 if (conn == null) { synchronized (this) { if (conn == null) { conn = createConn(url); } } } return conn; } } private Connection createConn(String url) throws Exception { // 创建JMS链接工厂 ActiveMQConnectionFactory connFactory = new ActiveMQConnectionFactory(url); // RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy(); // redeliveryPolicy.setMaximumRedeliveries(0); // RedeliveryPolicyMap redeliveryPolicyMap = new RedeliveryPolicyMap(); // redeliveryPolicyMap.setDefaultEntry(redeliveryPolicy); // connFactory.setRedeliveryPolicyMap(redeliveryPolicyMap); connFactory.setUseAsyncSend(true); // connFactory.setWarnAboutUnstartedConnectionTimeout(10L);//超时警告 // connFactory.setMaxThreadPoolSize(1); // TaskRunnerFactory taskRunnerFactory = new TaskRunnerFactory(); // taskRunnerFactory.setMaxIterationsPerRun(2); // taskRunnerFactory.setMaxThreadPoolSize(1); // taskRunnerFactory.setShutdownAwaitTermination(10); // connFactory.setSessionTaskRunner(taskRunnerFactory); // 创建消息连接 Connection tempConn = connFactory.createConnection(); // 启动连接 tempConn.start(); logger.error("创建MQ连接成功."); return tempConn; } public void closeConn() throws Exception { if (useDefaultConn && defaultConn != null) defaultConn.close(); else if (!useDefaultConn && conn != null) conn.close(); logger.error("MQ连接关闭."); } public boolean getUseDefaultConn() { return useDefaultConn; } public void setUseDefaultConn(boolean useDefaultConn) { this.useDefaultConn = useDefaultConn; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
2、客户端以订阅方式发送消息(MQPublish.java)
public class MQPublish { private static final Logger logger = LoggerFactory.getLogger(MQPublish.class); private MQMassageEntity entity; private MQPublicMeaageInvoke invoke; public void sendMess(MQMassageEntity entity) throws Exception { MQConnection ii = new MQConnection(); // ii.setUseDefaultConn(false); //取消默认连接 // ii.setUrl("failover:(tcp://localhost:61616)"); //手动设置MQ接收服务器地址 Connection conn = ii.getConn(); Session session = conn.createSession(true, Session.AUTO_ACKNOWLEDGE); // 订阅式消息模式,该模式中,除非消息被消费,否则在MQ未停止运行前,消息将一直保存 Topic topic = new ActiveMQTopic(MQStateAttribute.TOPIC_NAME); // 创建生产者订阅式消息对象 MessageProducer producer = session.createProducer(topic); // 设置消息持久化方式,为消息在MQ服务器中永久保存提供保障 // producer.setDeliveryMode(DeliveryMode.PERSISTENT); producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); // 创建MQ内置消息对象 MapMessage mm = session.createMapMessage(); mm.setString("Sender", entity.getSender()); mm.setString("Sendertime", MQUtils.myFormatDate(entity.getSendertime(), "yyyy-MM-dd HH:mm:ss")); mm.setString("Flowtype", entity.getFlowtype()); mm.setString("FlowID", entity.getFlowid()); mm.setString("Title", entity.getTitle()); mm.setString("Nodename", entity.getNodename()); mm.setString("Ptpurl", entity.getPtpurl()); mm.setString("Userid", entity.getUserid()); mm.setString("Creator", entity.getCreator()); mm.setString("Createdate", MQUtils.myFormatDate(entity.getCreatedate(), "yyyy-MM-dd")); mm.setString("Createtime", MQUtils.myFormatDate(entity.getCreatedate(), "HH:mm:ss")); mm.setString("Operatedate", MQUtils.myFormatDate(entity.getOperatedate(), "yyyy-MM-dd")); mm.setString("Operatetime", MQUtils.myFormatDate(entity.getOperatedate(), "HH:mm:ss")); mm.setString("Flowmess", entity.getFlowmess()); mm.setString("Viewtype", entity.getViewtype()); // mm.setString("Sender", "xxxxx系统"); // mm.setString("Sendertime", dt.toString("yyyy-MM-dd HH:mm:ss")); // mm.setString("Flowtype", "业务流程"); // mm.setString("FlowID", BaseUtils.uuid()); // mm.setString("Title", "请求标题"); // mm.setString("Nodename", "创建");//创建、批准、提交、归档 // mm.setString("Ptpurl","http://xxx.cn:8080/xxx/xxx/showDetailWithWorkFlow.do?businessKey=receiveorder:6e9aa422-8a01-4ad1-9155-7c3b80bbfeb3&taskId=46795"); // mm.setString("Userid", "xxx"); // mm.setString("Creator", "xxx"); // mm.setString("Createdate", dt.toString("yyyy-MM-dd")); // mm.setString("Createtime", dt.toString("HH:mm:ss")); // mm.setString("Operatedate", dt.toString("yyyy-MM-dd")); // mm.setString("Operatetime", dt.toString("HH:mm:ss")); // mm.setString("Flowmess", "1"); // mm.setString("Viewtype", "0"); // 生产者发送消息 producer.send(mm); session.commit(); producer.close(); session.close(); } public MQPublish(MQMassageEntity entity, MQPublishMeaageInvoke invoke) { this.entity = entity; this.invoke = invoke; } public MQPublish() { } //外部程序异步调用接口,invoke是额外要做的工作 public void start() { final Thread t = new Thread(new Runnable() { public void run() { try { invoke.doSomeThing(entity); logger.info("开始发送消息...."); } catch (Exception e) { e.printStackTrace(); logger.error("发送消息失败."); invoke.onFailure(entity); } invoke.onSuccess(entity); logger.info("发送消息成功."); } }); t.start(); } public MQMassageEntity getEntity() { return entity; } public void setEntity(MQMassageEntity entity) { this.entity = entity; } public MQPublicMeaageInvoke getInvoke() { return invoke; } public void setInvoke(MQPublishMeaageInvoke invoke) { this.invoke = invoke; } }
3、服务器端接收消息(MQReceiver.java)
public class MQReceiver { public static void main(String[] args) { // 连接MQ服务器 ConnectionFactory connectionFactory; Connection connection = null; Session session; MessageConsumer consumer; connectionFactory = new ActiveMQConnectionFactory("failover:(tcp://localhost:61616)"); try { // 创建连接 connection =connectionFactory.createConnection(); // 客户端id connection.setClientID("xxx-xx-64208-1385463895256-0:1"); connection.start(); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // 创建连接通道 Topic topic = session.createTopic(MQStateAttribute.TOPIC_NAME); consumer = session.createDurableSubscriber(topic, MQStateAttribute.TOPIC_NAME); // 开始接收消息 while(true){ MapMessage message = (MapMessage) consumer.receive(100000); if(null != message){ System.out.println("收到消息" + message.getString("Title")); System.out.println(message.getString("Sender")); System.out.println(message.getString("Sendertime")); System.out.println(message.getString("Flowtype")); System.out.println(message.getString("FlowID")); System.out.println(message.getString("Title")); System.out.println(message.getString("Nodename")); System.out.println(message.getString("Ptpurl")); System.out.println(message.getString("Userid")); System.out.println(message.getString("Creator")); System.out.println(message.getString("Createdate")); System.out.println(message.getString("Createtime")); System.out.println(message.getString("Operatedate")); System.out.println(message.getString("Operatetime")); System.out.println(message.getString("Flowmess")); System.out.println(message.getString("Viewtype")); }else break; } }catch(Exception e){ e.printStackTrace(); }finally { try { if (null != connection) connection.close(); } catch (Throwable ignore) { } } } }4、发送消息需要额外做操作的接口(MQPublishMessageInvoke.java)
/** * 消息发送的执行类 * <ol> * <li>dosomething要执行的动作</li> * <li>onsuccess执行成功后要执行的动作</li> * <li>onfailure执行失败后要执行的动作</li> * </ol> * @author zhaoxuewen * */ public abstract class MQPublishMeaageInvoke { protected void onSuccess(MQMassageEntity entity) { System.out.println("onSuccess"); } protected void onFailure(MQMassageEntity entity) { try { Thread.sleep(10000); new MQPublish().sendMess(entity); } catch (InterruptedException ie) { ie.printStackTrace(); } catch (Exception e1) { e1.printStackTrace(); } } protected void doSomeThing(MQMassageEntity entity) throws Exception{ new MQPublish().sendMess(entity); } }
5、消息体实体类(MQMassageEntity.java)
/** * MQ消息体实体类 * */ @Entity @Table(name = "MSG_MESSAGESEND") public class MQMassageEntity implements Serializable{ private static final long serialVersionUID = 1L; @Id @GeneratedValue(generator = "system-uuid") @GenericGenerator(strategy = "uuid", name = "system-uuid") @Column(name = "messagesendid") private String messagesendid; /** * 业务系统发来的消息,固定为“XX业务系统” */ private String sender; // 消息发送者 /** * 指业务系统系统发出此消息的时间,当系统出问题时,用于定位和分析,保证系统可靠性。 */ private Date sendertime; // 消息发送时间 /** * 可取以下值: * <ol> * <li>流程创建</li> * <li>流程待办事宜到达:需要提醒相关人处理(放入到OA的相关人员待办事宜中)</li> * <li>流程待办事宜已读:需要去掉new标志</li> * <li>流程已办:需要把此项流程放到已办列表中</li> * <li>流程已办结:表示流程已经流转完</li> * </ol> */ private String flowmess; // 事件类型 /** * 用于指示流程的类别 */ private String flowtype; // 流程分类 /** * * 表示流程在业务系统中的唯一ID */ private String flowid; // 流程ID /** * 流程实例的标题,如:报销流程-关于xx项目的报销申请,在OA中显示相应流程时使用 */ private String title;// 流程标题 /** * 如:业务系统的步骤名称,直接主管审批节点 */ private String nodename;// 流程当前节点名称 /** * 业务系统生成的到业务系统相关流程的URL链接,通过此URL应该可以直接打开此流程处理界面,而不需要重新登录业务系统。如:http:// */ private String ptpurl; // 到业务系统的URL /** * 流程的创建人 */ private String creator;// 创建人 /** * 流程的接收人 */ private String userid; // 接收人 /** * 流程的创建日期 */ private Date createdate;// 流程的创建日期时间 /** * 流程的更新日期 */ private Date operatedate;// 流程的更新日期时间 /** * 查看的状态 */ private String viewtype; /** * 标记 1发送,0未发送 */ private String flage; private String mess; /** * 发送次数 */ private int resendnumber; private String notice; }6、结束。经测试没有问题,可以正常发送接收消息。