把MQ部署到J2EE服务器,核心就三步:把MQ客户端组件放到应用服务器的全局类路径下,在应用服务器上配置JNDI资源指向MQ实例,业务代码通过JNDI查找连接工厂完成消息收发。下面拆开讲每一步怎么落,顺带把选型和排坑说明白。
mq部署到j2ee服务器的完整步骤
第一步:把MQ客户端jar包放对位置
很多人第一步就走错了,把activemq-all.jar丢进应用的WEB-INF/lib里,这么干在独立Tomcat下能跑,放到WebLogic或JBoss这些标准J2EE容器里就会出现类加载冲突,报一堆ClassNotFoundException或NoSuchMethodError。
正确做法是把jar包放进应用服务器的全局类路径:
- WebLogic:放到
$DOMAIN_HOME/lib目录下,然后重启整个Domain - JBoss/WildFly:放到
$JBOSS_HOME/standalone/lib目录,或用module方式在modules目录下创建module.xml - GlassFish:放到
$GLASSFISH_HOME/glassfish/domains/domain1/lib目录
放好之后别急着写代码,先确认应用服务器里的另一个JMS实现不会跟你抢类,WebLogic自己带了一套JMS,GlassFish也内置了OpenMQ,类路径里同时存在两个JMS实现时通常以全局lib里的优先,但某些版本需要改weblogic-application.xml里的prefer-application-packages配置来指定优先加载谁的类。
第二步:在应用服务器上配置JNDI资源
J2EE服务器管消息中间件的方式,是通过JNDI把连接工厂和消息队列暴露给应用,应用不直接感知MQ的IP和端口,只认JNDI名字。
以WebLogic为例操作路径如下:
- 登录WebLogic管理控制台,进入“服务”>“消息桥”或直接点“JMS模块”
- 新建一个JMS Module,比如叫
MQBridgeModule - 在该Module下创建ConnectionFactory,JNDI名写
jms/mqCF,服务器选择AdminServer或你的业务应用所在的目标服务器 - 创建Queue和Topic,JNDI名分别写
jms/orderQueue、jms/notifyTopic - 保存后激活变更
这里有个关键点:WebLogic的JMS默认绑定的是它自己内嵌的JMS服务,如果你要让ConnectionFactory走ActiveMQ,需要在创建完JMS Module后修改连接工厂提供程序的ClassName,把com.ibm或weblogic.jms开头的默认实现换成ActiveMQ的org.apache.activemq.jndi.ActiveMQInitialContextFactory并配置brokerURL。
JBoss那边相对直接,在standalone.xml的subsystem里加一个hornetq-activemq适配,或者用jndi.properties文件指定ActiveMQ的InitialContextFactory,再把连接工厂JNDI名绑定到java:/ConnectionFactory,每一步配置完要回控制台看一眼JNDI树里能不能查到jms/mqCF这个节点,查不到就说明没绑定成功。
行业共识认为,JNDI名称统一用java:comp/env/jms/前缀,能避免后续迁移容器时改代码,多数老项目出问题,都出在JNDI名字带不带java:comp/env/前缀这件事上。
第三步:业务代码里通过JNDI拿连接工厂
配置到位以后,业务侧代码就清爽了,不用硬编码MQ地址,用标准JNDI查找:
Context ctx = new InitialContext();
ConnectionFactory cf = (ConnectionFactory) ctx.lookup("java:comp/env/jms/mqCF");
Connection conn = cf.createConnection();
Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(session.createQueue("ORDER_QUEUE"));
producer.send(session.createTextMessage("订单已创建"));
这套代码在WebLogic、JBoss、GlassFish上行为一致,要注意的是连接和Session不要每次发消息都重新创建,连接池化交给应用服务器的连接工厂去管,业务侧复用一个连接即可,否则大量短连接会把MQ的连接数打满,据行业内公开的技术分享,这类问题在消息量稍大的系统里高频出现。
RabbitMQ和ActiveMQ在J2EE部署中怎么选
这个选择题,多数开发团队会在项目启动时纠结一阵,两者在J2EE容器里的部署路径和生态位置有明显差异,直接看对比:
| 对比项 | ActiveMQ | RabbitMQ |
|---|---|---|
| JMS原生支持 | 原生支持,JNDI配置标准 | 非原生JMS协议,需通过JMS客户端适配器接入 |
| J2EE容器内嵌适配 | 直接配置连接工厂即可 | 需要额外部署AMQP到JMS的转换层 |
| 事务支持 | 较好支持XA事务 | 事务模型与JTA对接较绕 |
| 部署复杂度 | 较低,单节点即可 | 略高,Erlang运行时环境 |
| 开源许可成本 | Apache 2.0,免费商用 | MPL 2.0,免费商用 |
这两年在国内企业环境里,RabbitMQ在互联网业务系统里用得更多,ActiveMQ则在传统政企和银行保险的项目里存量较大,原因很直接:互联网团队看重RabbitMQ的流控和延迟性能,传统J2EE项目看重ActiveMQ和JMS规范的契合度。
如果你是新项目且已经定了WebLogic加EJB这套标准J2EE路线,ActiveMQ省事,配好JNDI就能用,如果团队之前的消息中间件经验在RabbitMQ上,J2EE服务器只做业务承载,那就用RabbitMQ的JMS适配器,把brokerURL换成amqp://地址。
mq部署到weblogic时最容易踩到的两个坑
坑一:类加载冲突导致ClassNotFoundException
症状很典型:jar包放进去了,应用也部署了,一调ctx.lookup()就抛java.lang.ClassNotFoundException: javax.jms.ConnectionFactory。
原因是多个类加载器各带走了一份JMS的API,WebLogic自家的JMS实现和应用引入的ActiveMQ实现属于不同类加载器树,JNDI返回的实际类跟应用侧引用的接口不匹配。
解决办法:
- 将
javax.jms-api相关依赖设为provided,让容器统一提供 - 确保全局lib下只有一份MQ客户端的实现
- 在
weblogic-application.xml里加prefer-application-packages配置,排除冲突包
坑二:EJB里发消息事务超时
在EJB的容器管理事务中调用producer.send(),事务管理器会把MQ资源纳入分布式事务,整体响应时间变长,多数情况下,事务超时阈值默认设置成120秒,但MQ事务参与会让EJB调用耗时飙升,超过阈值直接回滚,消息也跟着丢了。
实践上常见的做法是:把发消息的方法标上@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW),让消息发送在独立事务里完成,这样即使外层事务回滚,消息也已经进队列,保证消息不丢,但要注意消息语义从“和业务同生共死”变成了“业务失败消息也可能已发”,需要业务侧做补偿处理。
mq部署到j2ee服务器之后怎么验证部署成功
资源配完、代码写完,别直接上线,按下面三步验证一遍,能挡住大部分问题:
- 查JNDI树:打开应用服务器的管理控制台,在JNDI树里找到jms/mqCF节点,能展开看到连接工厂属性说明绑定成功
- 跑一次同步收发:写个测试类,lookup连接工厂后发一条消息,再用同一个Queue的消费方接收,验证消息体内容一致
- 看事务日志:在EJB里调一次发送,观察服务器日志里JMS事务的commit和rollback记录,看到commit关键字就说明事务接入正常
这三个验证点全部通过,基本可以判断MQ在J2EE服务器里的接入是稳定的,后期如果业务量涨上来,再监控连接池占用和消息积压量这些运行期指标。
关于mq部署到j2ee服务器的Q&A
问:Tomcat算J2EE服务器吗?MQ能直接部署上去吗?
Tomcat只实现了Servlet和JSP规范,不是完整的J2EE容器,不支持EJB和完整的JTA事务,但MQ的JMS客户端照样能跑,只是没有JNDI和事务管理这些容器能力,需要自己在代码里创建连接池和事务边界,项目规模小或者只是对内做异步解耦,用Tomcat加ActiveMQ也没问题,但事务一致性要求高的场景,还是建议上完整的J2EE服务器。
问:mq部署到JBoss和WebLogic的步骤差别大吗?
大方向一样,都是放jar包、配JNDI、写代码这三步,细节差别在于:JBoss用standalone.xml配置子系统,模块加载方式和目录结构与WebLogic不同,且WildFly后续版本对JMS的配置方式又变过一轮,WebLogic则以控制台操作为主,代码层面如果都走标准JNDI查找,两个平台间迁移基本不用改业务类。
问:mq部署到J2EE服务器后报javax.jms.JMSException: Could not connect to broker,怎么排查?
先确认全局lib里的jar包版本和MQ服务端版本兼容,再去MQ服务端看日志中是否拒绝了来自应用服务器IP的连接,最后检查防火墙端口和brokerURL的主机名是否解析正确,这条异常链路上,连接被网络层挡掉的情况占一大半,查完网络再看认证和版本兼容性。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/790563.html


