最新项目上应用到ActiveMQ开源框架,其中也发现了一些问题,总结了一些浅薄的经验,本着开源,其他程序员少走一些弯路的宗旨,特与人分享,有好的方法,好的经验可以互相分享。
上正文(因为项目使用grails框架groovy语言,但是其他语言大致想通):
class ChargeRespReceiverService {
// ConnectionFactory :连接工厂,JMS 用它创建连接
private static ConnectionFactory connectionFactory;
// Connection
private static Connection topicConn;
// Session: 一个响应发送或接收消息的线程
private static Session topicSession;
// Topic: 响应消息的目的地;消息发送给谁.
private static Topic topicPay = null;
// MessageConsumer:响应消息接收者
private static MessageConsumer topicConsumer;
//使用静态块初始化JMS连接
static {
initJms();
}
/**
* 初始化
*/
static void initJms() {
// 构造ConnectionFactory实例对象,此处采用ActiveMq的实现 Constants.JMS_URL--JMS链接 Constants.PAY_TOPIC--TOPIC名称 "myChargeClient" 为注册到MQ的客户端ID,取唯一值
try {
connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, Constants.JMS_URL);
topicConn = connectionFactory.createConnection();
topicConn.setClientID("myChargeClient");
//true为开启事务模式,onMessage方法完全执行成功之后签名,过程中如果出现异常自动回滚,消息还在,false为接收之后自动签名,没有回滚
//还有一点要注意,Session.AUTO_ACKNOWLEDGE为MQ自动签名机制,但是如果MQ中如果有之前已经序列化的内容,经测试MQ不会自动去签名,此时需要调用topicSession.commit()方法进行手动签名
topicSession = topicConn.createSession(true, Session.AUTO_ACKNOWLEDGE);
topicPay = topicSession.createTopic(Constants.PAY_TOPIC);
topicConn.start();
} catch (JMSException e) {
e.printStackTrace();
if (topicConn != null) {
try {
topicConn.close();
topicConn = null;
} catch (JMSException e1) {
e1.printStackTrace();
}
}
}
}
/**
* 接收消息
* @param selector
* @return
* @throws Exception
*/
static synchronized MessageConsumer receiveResp(String selector) throws Exception {
//"chargeClient"为注册到MQ中的用户名,这个建议不要重复,方便在管理后台查看消息数量
topicConsumer = topicSession.createDurableSubscriber(topicPay, "chargeClient", selector, Boolean.FALSE);
return topicConsumer;
}
/**
* 监听MQ的消息
*/
public void chargeRespReceive() {
println "充值服务监听启动...........";
try {
//此处进行选择,过滤掉不需要的消息、这个机制非常好
String selector = "receiver = 'charge'";
MessageConsumer consumer = receiveResp(selector);
String s = consumer.getMessageSelector();
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message arg0) {
MapMessage msg = (MapMessage) arg0;
try {
String srvCode = msg.getString("srvCode");
String outOrder = msg.getString("outOrder");
String feedback = msg.getString("feedback");
String reason = msg.getString("reason");
String chanelId = msg.getString("chanelId");
Charge charge = Charge.get(outOrder as long);
if (null != charge) {
charge.tradeFeedback = feedback;
charge.setTradeReason(reason==null?"":reason); //reason;
charge.setTradeStatus("2");//处理完毕
//充值已经操作成功
if ("succ".equals(charge.tradeFeedback)) {
charge.tradeFeedback = chargeService.chargeCustomerInfo(charge, chanelId) ? "succ" : "fail";//成功之后需要处理账务
} else if ("fail".equals(charge.tradeFeedback)) { //打款平台处理失败
charge.tradeFeedback = "fail";
sendChargeFeedBackEmail(charge);//失败发送Email
}
//此处要注意,在onMessage这个内部类的方法内,或者线程内使用Hibernate方式会出现事务问题,这个要加上相应的事务
//在grails中Domain的事务方式比较方便例如:Charge.withTransaction {status -> //开启事务}
//Charge.update(charge);
Charge.executeUpdate("update Charge set tradeFeedback = '"+charge.tradeFeedback+"', tradeReason = '"+charge.tradeReason+"', tradeStatus = "+charge.tradeStatus+", tradeDonedate = sysdate where id = "+charge.id as String);
}
//这个就是提到的,如果接收已经序列化到MQ服务器本地文件,或者数据库中的消息时会发现消息取出之后并没有减少的问题,
//手动提交之后解决该问题(相关序列化的文章,在网上有很多)
topicSession.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
});
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
最后的最后,别忘了,启动这个监听
在BootStrap.groovy中加入启动
//启动充值反馈监听
chargeRespReceiverService.chargeRespReceive();