Spring與ActiveMQ整合

x號開發者發表於2019-01-08

在實際的專案中如果使用原生的ActiveMQ API開發會比較麻煩,因為需要建立連線工廠,建立連線等,我們應該使用一個模板來做這些繁瑣的事情,Spring幫我們做了!

Spring提供了對JMS的支援,需要新增Spring支援jms的包,如下:

複製程式碼
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jms</artifactId>
    <version>4.0.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-pool</artifactId>
    <version>5.9.0</version>
</dependency>
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-all</artifactId>
    <version>5.9.0</version>
</dependency>
複製程式碼

在spring-amq.xml中配置JmsTemplate(這樣的配置沒啥問題,在實際的專案中就是這樣配置的)

複製程式碼
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:component-scan base-package="com.winner.spring"/>

    <bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory"
          destroy-method="stop">
        <property name="connectionFactory">
            <bean class="org.apache.activemq.ActiveMQConnectionFactory">
                <property name="brokerURL">
                    <value>tcp://192.168.0.129:61616</value>
                </property>
            </bean>
        </property>
        <property name="maxConnections" value="100"></property>
    </bean>

    <!--使用快取可以提升效率-->
    <bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
        <property name="targetConnectionFactory" ref="jmsFactory"/>
        <property name="sessionCacheSize" value="1"/>
    </bean>

    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="cachingConnectionFactory"/>
        <property name="messageConverter">
            <bean class="org.springframework.jms.support.converter.SimpleMessageConverter"/>
        </property>
    </bean>

    <!--測試Queue,佇列的名字是spring-queue-->
    <bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue">
        <!--<constructor-arg index="0" value="spring-queue"/>-->
        <constructor-arg name="name" value="spring-queue"/>
    </bean>

    <!--測試Topic-->
    <bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">
        <constructor-arg index="0" value="spring-topic"/>
    </bean>

</beans>
複製程式碼

生產者

複製程式碼
@Service
public class AMQSenderServiceImpl implements AmqSenderService {

    private static final Logger logger = LoggerFactory.getLogger(AMQSenderServiceImpl.class);

    @Resource(name = "jmsTemplate")
    private JmsTemplate jmsTemplate;

    //目的地佇列的明證,我們要向這個佇列傳送訊息
    @Resource(name = "destinationQueue")
    private Destination destination;

    //向特定的佇列傳送訊息
    @Override
    public void sendMsg(MqParamDto mqParamDto) {
        final String msg = JSON.toJSONString(mqParamDto);
        try {
            logger.info("將要向佇列{}傳送的訊息msg:{}", destination, msg);
            jmsTemplate.send(destination, new MessageCreator() {
                @Override
                public Message createMessage(Session session) throws JMSException {
                    return session.createTextMessage(msg);
                }
            });

        } catch (Exception ex) {
            logger.error("向佇列{}傳送訊息失敗,訊息為:{}", destination, msg);
        }

    }
}
複製程式碼

執行結果:

如果是Topic的話就換一下,下面的spring-topic是主題的名字。

<bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">
    <constructor-arg index="0" value="spring-topic" />
</bean>

其他不用改!

如果想要在Spring中配置消費者的話,就不需要再啟動接收的客戶端了,這樣在測試的時候可以不需要寫消費者的程式碼,因為我們要麼是生產者要麼是消費者!

可以通過配置一個listener來實現,實際專案中採用的是這種方式

複製程式碼
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory"
          destroy-method="stop">
        <property name="connectionFactory">
            <bean class="org.apache.activemq.ActiveMQConnectionFactory">
                <property name="brokerURL">
                    <value>tcp://192.168.0.129:61616</value>
                </property>
            </bean>
        </property>
        <property name="maxConnections" value="100"></property>
    </bean>

    <!--使用快取可以提升效率-->
    <bean id="cachingConnectionFactory"
          class="org.springframework.jms.connection.CachingConnectionFactory">
        <property name="targetConnectionFactory" ref="jmsFactory"/>
        <property name="sessionCacheSize" value="1"/>
    </bean>

    <!--測試Queue,佇列的名字是spring-queue-->
    <bean id="destinationQueue" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg index="0" value="spring-queue"/>
    </bean>

    <!--測試Topic-->
    <bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">
        <constructor-arg index="0" value="spring-topic"/>
    </bean>

    <bean id="jmsContainer"
          class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="cachingConnectionFactory"/>
        <property name="destination" ref="destinationQueue"/>
        <property name="messageListener" ref="messageListener"/>
    </bean>

    <!--訊息監聽器-->
    <bean id="messageListener" class="com.winner.spring.MyMessageListener">
    </bean>

</beans>
複製程式碼

 監聽器:

複製程式碼
public class MyMessageListener implements MessageListener {

    @Override
    public void onMessage(Message msg) {
        if (msg instanceof TextMessage) {
            try {
                TextMessage txtMsg = (TextMessage) msg;
                String message = txtMsg.getText();
                //實際專案中拿到String型別的message(通常是JSON字串)之後,
                //會進行反序列化成物件,做進一步的處理
                System.out.println("receive txt msg===" + message);
            } catch (JMSException e) {
                throw new RuntimeException(e);
            }
        } else {
            throw new IllegalArgumentException("Message must be of type TextMessage");
        }
    }
}
複製程式碼

不需要寫消費者的程式碼就可以知道訊息有沒有推送成功

 

 ActiveMQ結合Spring開發最佳實踐和建議:

1:Camel框架支援大量的企業整合模式,可以大大簡化整合元件間的大量服務和複雜的訊息流。而Spring框架更注重簡單性,僅僅支援基本的最佳實踐。
2:Spring訊息傳送的核心架構是JmsTemplate,隔離了像開啟、關閉Session和Producer的繁瑣操作,因此應用開發人員僅僅需要關注實際的業務邏輯。但是
JmsTemplate損害了ActiveMQ的PooledConnectionFactory對session和訊息producer的快取機制而帶來的效能提升。
3:新的Spring裡面,可以設定org.springframework.jms.connection.CachingConnectionFactory的sessionCacheSize,或者乾脆使用ActiveMQ的PooledConnectionFactory
4:不建議使用JmsTemplate的receive()呼叫,因為在JmsTemplate上的所有呼叫都是同步的,這意味著呼叫執行緒需要被阻塞,直到方法返回,這對效能影響很大
5:請使用DefaultMessageListenerContainer,它允許非同步接收訊息並快取session和訊息consumer,而且還可以根據訊息數量動態的增加或縮減監聽器的數量

相關文章