需要澄清JMS与ActiveMQ bean /资源配置

Joh*_*nko 5 java activemq-classic jms glassfish ejb-3.1

在如何使用JMS资源以及在注释activationConfig上正确设置方面似乎存在一些不一致.@ActivationConfigProperty@MessageDriven

首先,这是我的资源配置(glassfish-resources.xml,但可以翻译成其他部署描述符).这适用于Glassfish(asadmin add-resources glassfish-resources.xml)以及ActiveMQ资源适配器:

<?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>
Run Code Online (Sandbox Code Playgroud)

这是我的消息提供者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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在定义@MessageDriven bean 时会出现混乱.以下使用mappedName引发异常:

@MessageDriven(mappedName = "jms/queue/myApp")
public class MessageBean implements MessageListener
Run Code Online (Sandbox Code Playgroud)

警告:RAR8000:类中不存在方法setName:org.apache.activemq.command.ActiveMQQueue警告:RAR7097:属性中没有setter方法org.apache.activemq.com.ActiveMQQueue类中的名称信息:访问未访问的引用信息:访问未访问的引用警告:RAR8501:ra [activemq-rar],activationSpecClass [org.apache.activemq.ra.ActiveMQActivationSpec]的端点激活期间出现异常: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
Run Code Online (Sandbox Code Playgroud)

我需要提供一个glassfish-ejb-jar.xml告诉容器使用ActiveMQ资源,否则我得到一个java.lang.ClassCastException:

警告:RAR8501:ra [jmsra],activationSpecClass [com.sun.messaging.jms.ra.ActivationSpec]的端点激活期间发生异常: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>
Run Code Online (Sandbox Code Playgroud)

因此,生产者如何使用资源(JNDI)和消费者如何(XML + @ActivationConfigProperty)之间似乎存在一些不一致.此外,EE7 ActivationConfigProperty属性似乎不起作用.例如,使用destinationLookup不会查找目标,我不得不使用ActiveMQ的destination属性.

ActiveMQ 列出以下激活规范属性:

acknowledgeMode(要使用的JMS确认模式.有效值为:自动确认或Dups-ok-acknowledge)

clientId(要使用的JMS客户端ID(仅对持久主题非常必需))

destinationType(目标类型;队列或主题)

destination(目标名称(队列或主题名称))

enableBatch(用于启用事务批处理以提高性能)

maxMessagesPerBatch(每个事务批处理的消息数)

maxMessagesPerSessions(这实际上是订阅的预取大小.(是的,命名不当).)

maxSessions(要使用的最大并发会话数)

messageSelector(用于订阅的JMS消息选择器,用于执行基于内容的路由过滤消息)

noLocal(仅限主题订阅;表示本地发布的消息是否应包含在订阅中)

password(JMS连接的密码)

subscriptionDurability(是否需要持久(主题)订阅.有效值为:Durable或NonDurable)

subscriptionName(持久订阅者的名称.仅用于持久主题并与clientID结合使用以唯一标识持久主题订阅)

userName (JMS连接的用户)

useRAManagedTransaction (通常,资源适配器将消息传递到由容器管理的端点.通常,此容器喜欢是想要控制正在传递入站消息的事务的容器.但有时,您希望传递更简单的容器系统,它不会控制入站事务.在这些情况下,如果将useRAManagedTransaction设置为true,如果没有从MessageListener生成异常,则资源适配器将提交事务,如果抛出异常则返回.)

initialRedeliveryDelay(重新开始之前的延迟.也可以在ResourceAdapter上配置)

maximumRedeliveries(最大重传次数或-1,没有最大值.也可在ResourceAdapter上配置)

redeliveryBackOffMultiplier(如果启用了指数后退,则使用的乘数.也可以在ResourceAdapter上配置)

redeliveryUseExponentialBackOff(要启用指数退避.也可以在ResourceAdapter useJndi上配置,如果为true,则不使用false,使用destination作为jndi名称)

Java EE7规范列出了以下激活规范属性:

acknowledgeMode(此属性用于指定使用Bean管理的事务划分时消息传递的JMS确认模式.其值为Auto_acknowledge或Dups_ok_acknowledge.如果未指定此属性,则假定为JMS AUTO_ACKNOWLEDGE语义.

messageSelector(此属性用于指定用于确定JMS消息驱动Bean要接收哪些消息的JMS消息选择器)

destinationType(此属性用于指定消息驱动Bean是用于队列还是主题.值必须是javax.jms.Queue或javax.jms.Topic.)

destinationLookup(此属性用于指定JMS消息驱动Bean从中接收消息的JMS队列或主题.)

connectionFactoryLookup(此属性用于指定将用于连接到JMS消息传递接收消息的JMS提供程序的JMS连接工厂.)

subscriptionDurability(如果消息驱动bean旨在与主题一起使用,则此属性可用于指示是否应使用持久订阅或非持久订阅.此属性的值必须是Durable或NonDurable)

subscriptionName(如果消息驱动的bean旨在与Topic一起使用,则此属性用于指定持久订阅的名称,并且bean提供程序已指示应使用持久订阅.)

clientId(此属性用于指定在连接到JMS消息传递来自JMS消息传递bean的JMS提供程序时将使用的JMS客户端标识符.如果未指定此属性,则将取消设置客户端标识符. )

在只有@Inject点和jndi查找的生产者和消费者中使用ActiveMQ资源的正确方法是什么?我想避免使用glassfish-ejb-jar.xml并使用@ActivationConfigProperty.来定义队列名称.

99S*_*ono 4

是的,每个应用程序服务器的做法都有些不同。更重要的是,它们的做法有所不同,不是您如何配置它 - 这部分很简单,而是当您期望 JMS 服务器提供 SLA(例如有序消息处理)时的运行时行为 - 即使在失败的情况下也是如此。

例如,如果您有一个业务关键流程,其中消息 2 只能在消息 1 之后处理。并且您的消息 1 失败,并且您希望重试,但您还配置了 200 毫秒的重新传递延迟。一些应用程序服务器默认会认为:消息 1 失败,我在 200 毫秒内重试,跳到下一条消息...并且 kabum,业务流程已死,因为您对有序消息消费的期望被违反了。

通常,好的 JMS 服务器能够以这样的方式配置它,以满足您所需的 SLA……但这很棘手。

通常,您应该通过注释在 MDB 上配置跨多个应用程序服务器交叉工作的任何属性。通常,JNDI 命名可以工作 - 但这很棘手,因为 JNDI 高度依赖于容器。属性例如: - 激活属性:destinationType = javax.jms.Topic

这是相当标准的,所以你可以通过注释来添加它。

但是当您遇到棘手的方面时,例如指定连接工厂以连接到目标。或者您是否应该允许 JMS 服务器一次批量读取 N 条消息,或者您是否想强制一次读取一条消息,等等...这高度依赖于您的容器,您将希望不通过注释来配置它通过 ejb 部署描述符。

例如,在 weblogic 中,您需要使用: weblogic-ejb-jar.xml 来微调诸如 JNDI 名称之类的内容来访问队列、max-beans-in-free-pool 等...

在使用 ActiveMQ 的 Wildfly 中,您可能希望使用:jboss-ejb3.xml

部署描述符来执行此操作。

因此,通过注释 - 您应该跨容器交叉等效元数据的最大公分母。在部署描述符中,您可以使用缺少的元数据来丰富配置。

好的应用程序服务器将始终执行合并过程,将 MDB 上的元数据与部署描述符上的元数据组合起来。当发生冲突时,它们接管部署描述符上的配置。

等等。

所以,您确实需要在容器支持的部署描述符中调整 pert 容器。在您的 Java 代码中,您应该只保留交叉兼容的元数据。

最后,在使用不同 JMS 服务器实现的不同应用程序服务器上获取消息处理的确切 JMS 行为……是相当棘手的。

除非您有不关心有序处理的基本场景,否则您有多个 MDB 在队列上并行运行,因为没有发生之前的关系......那么让一个草率的配置工作是微不足道的。