在如何使用JMS资源以及在 @MessageDriven
注释上使用正确的 @ActivationConfigProperty
设置 activationConfig
似乎存在一些不一致 .
首先,这是我的资源配置( glassfish-resources.xml ,但可以翻译成其他部署描述符) . 这适用于Glassfish( asadmin add-resources glassfish-resources.xml
)以及ActiveMQ Resource Adapter:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
<resource-adapter-config name="activemq-rar"
thread-pool-ids="thread-pool-1"
resource-adapter-name="activemq-rar">
<property name="ServerUrl" value="tcp://localhost:61616"/>
<property name="UserName" value="admin"/>
<property name="Password" value="admin"/>
<property name="UseInboundSession" value="false"/>
</resource-adapter-config>
<admin-object-resource enabled="true"
jndi-name="jms/queue/myApp"
object-type="user"
res-adapter="activemq-rar"
res-type="javax.jms.Queue">
<description>MyApp JMS Queue</description>
<property name="Name" value="myAppAMQ"/>
<property name="PhysicalName" value="myAppAMQ"/>
</admin-object-resource>
<connector-resource enabled="true"
jndi-name="jms/factory/myApp"
object-type="user"
pool-name="jms/factoryPool/myApp">
<description>MyApp Connection Factory</description>
<property name="Name" value="myAppFactory"/>
</connector-resource>
<connector-connection-pool associate-with-thread="false"
connection-creation-retry-attempts="0"
connection-creation-retry-interval-in-seconds="10"
connection-definition-name="javax.jms.QueueConnectionFactory"
connection-leak-reclaim="false"
connection-leak-timeout-in-seconds="0"
fail-all-connections="false"
idle-timeout-in-seconds="300"
is-connection-validation-required="false"
lazy-connection-association="false"
lazy-connection-enlistment="false"
match-connections="true"
max-connection-usage-count="0"
max-pool-size="32"
max-wait-time-in-millis="60000"
name="jms/factoryPool/myApp"
ping="false"
pool-resize-quantity="2"
pooling="true"
resource-adapter-name="activemq-rar"
steady-pool-size="8"
validate-atmost-once-period-in-seconds="0"/>
</resources>
这是我的消息提供者bean . 您会注意到找到了JNDI名称并且使用了ActiveMQ资源而没有错误,消息被发送到正确的队列:
@Stateless
@LocalBean
public class ServicesHandlerBean {
@Resource(mappedName = "jms/queue/myApp")
private Queue queue;
@Resource(mappedName = "jms/factory/myApp")
private ConnectionFactory factory;
public void sendJMSMessage(MessageConfig messageData) throws JMSException {
Connection connection = null;
Session session = null;
try {
connection = factory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer messageProducer = session.createProducer(queue);
messageProducer.send(createJMSMessage(session, messageData));
} finally {
if (session != null) {
try {
session.close();
} catch (JMSException e) {
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Cannot close session", e);
}
}
if (connection != null) {
connection.close();
}
}
}
}
定义 @MessageDriven bean时会出现混乱 . 以下使用 mappedName 抛出异常:
@MessageDriven(mappedName = "jms/queue/myApp")
public class MessageBean implements MessageListener
警告:RAR8000:类中不存在方法setName:org.apache.activemq.command.ActiveMQQueue警告:RAR7097:类属性中没有setter方法org.apache.activemq.com.ActiveMQQueue类中的Name信息:访问未访问的引用信息:访问未访问的引用警告:RAR8501:ra [activemq-rar],activationSpecClass [org.apache.activemq.ra.ActiveMQActivationSpec]的 endpoints 激活期间出现异常:javax.resource.ResourceException:未知目标类型:null严重: MDB00017:[InvoiceProductionMessageBean]:创建消息驱动的bean容器时出现异常:[java.lang.Exception]严重:java.lang.Exception
我被迫定义我的MDB:
@MessageDriven(
activationConfig = {
@ActivationConfigProperty(propertyName = "connectionFactoryLookup", propertyValue = "jms/factory/myApp"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "myAppAMQ"),
@ActivationConfigProperty(propertyName = "messageSelector", propertyValue = " JMSType = 'TypeA' "),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
}
)
public class MessageBean implements MessageListener
我需要提供 glassfish-ejb-jar.xml 告诉容器使用ActiveMQ资源,否则我得到一个 java.lang.ClassCastException
:
警告:RAR8501:ra [jmsra],activationSpecClass [com.sun.messaging.jms.ra.ActivationSpec]的 endpoints 激活期间出现异常:java.lang.ClassCastException:org.apache.activemq.ra.ActiveMQConnectionFactory无法强制转换为com .sun.messaging.jms.ra.DirectConnectionFactory严重:MDB00017:[MessageBean]:创建消息驱动的bean容器时出现异常:[java.lang.Exception]严重:java.lang.Exception
glassfish-ejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-ejb-jar PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 EJB 3.1//EN" "http://glassfish.org/dtds/glassfish-ejb-jar_3_1-1.dtd">
<glassfish-ejb-jar>
<enterprise-beans>
<ejb>
<ejb-name>MessageBean</ejb-name>
<mdb-resource-adapter>
<resource-adapter-mid>activemq-rar</resource-adapter-mid>
</mdb-resource-adapter>
</ejb>
</enterprise-beans>
</glassfish-ejb-jar>
因此, 生产环境 者如何使用资源(JNDI)和消费者如何使用(XML @ActivationConfigProperty
)之间似乎存在一些不一致 . 此外,EE7 ActivationConfigProperty属性似乎不起作用 . 例如,使用 destinationLookup doesn 't lookup the destination, and I' m强制使用ActiveMQ的 destination
属性 .
ActiveMQ lists the following Activation Spec Properties:
acknowledgeMode(要使用的JMS确认模式 . 有效值为:自动确认或Dups-ok-acknowledge)clientId(要使用的JMS客户端ID(仅对持久主题非常必需))destinationType(目标类型;队列或主题)destination(目标名称(队列或主题名称))enableBatch(用于启用事务批处理以提高性能)maxMessagesPerBatch(每个事务批处理的消息数)maxMessagesPerSessions(这实际上是订阅的预取大小 . ,错误地命名) . )maxSessions(要使用的最大并发会话数)messageSelector(用于订阅以执行基于内容的路由过滤消息的JMS消息选择器)noLocal(仅对主题订阅需要;指示本地发布的消息)应该包含在订阅中或不包含)密码(JMS连接的密码)subscriptionDurability(持久性(主题)订阅是否为r) equired . 有效值为:Durable或NonDurable)subscriptionName(持久订阅者的名称 . 仅用于持久主题并与clientID结合以唯一标识持久主题订阅)userName(用于JMS连接的用户)useRAManagedTransaction(通常是资源)适配器将消息传递到由容器管理的 endpoints . 通常,此容器喜欢是想要控制正在传递入站消息的事务的容器 . 但有时,您希望传递给更简单的容器系统在这些情况下,如果将useRAManagedTransaction设置为true,如果没有从MessageListener生成异常,则资源适配器将提交事务,如果抛出异常则返回回滚 . )initialRedeliveryDelay(重新启动之前的延迟) . 也可以在ResourceAdapter上配置)maximumRedeliveries(最大重传次数或-1,无m) aximum . 也可以在ResourceAdapter上配置redeliveryBackOffMultiplier(如果启用了指数后退,则使用的乘数 . 也可以在ResourceAdapter)redeliveryUseExponentialBackOff(要启用指数退避 . 也可以在ResourceAdapter useJndi上配置,如果为true,则不使用false,使用destination作为jndi名称)
Java EE7规范lists the following Activation Spec Properties:
acknowledgeMode(此属性用于指定使用Bean管理的事务划分时消息传递的JMS确认模式 . 其值为Auto_acknowledge或Dups_ok_acknowledge . 如果未指定此属性,则假定为JMS AUTO_ACKNOWLEDGE语义.messageSelector(此属性用于指定JMS消息选择器,用于确定JMS消息驱动Bean要接收哪些消息)destinationType(此属性用于指定消息驱动Bean是用于队列还是主题 . value必须是javax.jms.Queue或javax.jms.Topic . )destinationLookup(此属性用于指定JMS消息驱动bean从中接收消息的JMS队列或主题 . )connectionFactoryLookup(使用此属性)指定将用于连接到JMS消息传递接收消息的JMS提供程序的JMS连接工厂 . )subscriptionDurability(If消息驱动Bean旨在与主题一起使用,此属性可用于指示是否应使用持久订阅或非持久订阅 . 此属性的值必须是Durable或NonDurable)subscriptionName(如果消息驱动的bean旨在与主题一起使用,则此属性用于指定持久订阅的名称,并且bean提供程序已指示持久应该使用订阅 . )clientId(此属性用于指定在连接到JMS消息传递来自JMS消息传递bean的JMS提供程序时将使用的JMS客户端标识符 . 如果未指定此属性,则客户端标识符将被取消设置 . )
在只有@Inject点和jndi查找的 生产环境 者和消费者中使用ActiveMQ资源的正确方法是什么?我想避免使用glassfish-ejb-jar.xml并使用 @ActivationConfigProperty
定义队列名称 .
2 回答
是的,每个应用服务器的工作方式都有所不同 . 更重要的是,它们的不同之处不在于您如何配置它 - 这部分很简单,但是当您期望从JMS服务器获得SLA(例如有序消息处理)时,即使在出现故障的情况下,也会出现运行时行为 .
例如,如果您有一个关键业务流程,其中消息2只能在消息1之后处理 . 并且您的消息1失败,并且您希望重试,但您还配置了200毫秒的重新传递延迟 . 一些应用程序服务器,默认情况下会认为:消息1失败,我在200毫秒内重试,跳转到下一条消息......而kabum,业务流程已经死亡,因为您违反了有序消息消耗的预测 .
通常,优秀的JMS服务器能够以满足您所需的SLA的方式对其进行配置......但这很棘手 .
通常,您应该在MDB上通过注释配置任何跨多个应用程序服务器交叉工作的属性 . 通常,JNDI命名可以工作 - 但它很棘手,因为JNDI高度依赖于容器 . 属性如: - Activation Propertu:destinationType = javax.jms.Topic
这是非常标准的,所以你可以通过注释 .
但是当你遇到棘手的方面时,比如指定连接工厂连接到目的地 . 或者您应该允许JMS服务器一次批量读取N个消息,或者您是否希望一次强制一次,等等...这很大程度上取决于您的容器,并且您不希望通过注释来配置它通过ejb部署描述符 .
例如,在weblogic中,您可能希望使用:weblogic-ejb-jar.xml要微调诸如JNDI名称之类的东西以访问队列,max-beans-in-free-pool等...
在Wildfly中,使用ActiveMQ,您可能不希望使用:jboss-ejb3.xml
部署描述符来执行此操作 .
因此,通过注释 - 您应该在容器上使用交叉切割等效元数据的公分母 . 在部署描述符中,您使用缺少元数据来丰富配置 .
好的应用程序服务器将始终执行合并过程,它们将MDB上的元数据与部署描述符上的元数据组合在一起 . 当发生碰撞时,他们接管部署描述符上的配置 .
等等 .
所以有些人,你真的需要在容器支持的部署描述符中调整pert容器 . 在您的Java代码中,您应该只保留横切兼容的元数据 .
最后,在使用不同JMS服务器实现的不同应用程序服务器上获取消息处理的确切JMS行为......非常棘手 .
除非你有一个不关心有序处理的基本scneario,你在队列上有多个并行运行的MDB,因为在关系之前没有发生...然后让slopy配置工作是微不足道的 .
似乎所有服务器都有不同的做法 .