ActiveMQ 实战

 

JMS - ActiveMQ 实战

JMS(JAVA Message Service,java 消息服务)API 是一个消息服务的标准或者说是规范,允许应用程序组件基于 JavaEE 平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。

基本概念

JMS 是 java 的消息服务,JMS 的客户端之间可以通过 JMS 服务进行异步的消息传输。

消息模型分为:
点对点:Point-to-Point(P2P)
发布订阅:Publish/Subscribe(Pub/Sub)

 

JMS 编程模型

1.ConnectionFactory

针对两种不同的jms消息模型,分别有 QueueConnectionFactory 和 TopicConnectionFactory 两种。

2.Destination

Destination 是消息生产者的消息发送目标或者说消息消费者的消息来源。它是某个队列(Queue)或某个主题(Topic)。所以,Destination 就是两种类型的对象:Queue、Topic。

3.Connection

Connection 表示在客户端和 JMS 系统之间建立的链接(对TCP/IP socket的包装)。Connection 可以产生一个或多个 Session。跟 ConnectionFactory 一样,Connection也有两种类型:QueueConnection 和 TopicConnection。

4.Session

Session 是我们操作消息的接口。可以通过session创建生产者、消费者、消息等。Session提供了事务的功能。当我们需要使用 session 发送/接收多个消息时,可以将这些发送/接收动作放到一个事务中。同样,也分为 QueueSession 和 TopicSession。

5.消息生产者

消息生产者由Session创建,并用于将消息发送到Destination。同样,消息生产者分两种类型:QueueSender 和 TopicPublisher。可以调用消息生产者的方法(send 或 publish 方法)发送消息。

6.消息消费者

消息消费者由 Session 创建,用于接收被发送到Destination的消息。两种类型:QueueReceiver 和 TopicSubscriber。可分别通过 session 的 createReceiver(Queue) 或 createSubscriber(Topic) 来创建。当然,也可以 session 的 creatDurableSubscriber 方法来创建持久化的订阅者。

7.MessageListener

消息监听器。如果注册了消息监听器,一旦消息到达,将自动调用监听器的onMessage方法。

 

ActiveMQ

Apache ActiveMQ 是一款最流行的,能力强劲的开源消息中间件。ActiveMQ是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider 实现。ActiveMQ 默认使用的 TCP 连接端口是61616。提供一个用于监控ActiveMQ 的应用,默认地址:http://127.0.0.1:8161/admin/。 

 

点对点

每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。如果希望发送的每个消息都应该被成功处理的话,那么需要 P2P 模式。特点如下:
1.每个消息只有一个消费者,一旦被消费,消息就不再在消息队列中。
2.发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列。
3.接收者在成功接收消息之后需向队列应答成功。

生产者

package com.mq.p2p;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.text.MessageFormat;
/**
 * 类  名:JMSProducer
 * 类描述:点对点 - 生产者
 */
public class JMSProducer {
    private static final String USERNAME = "hq";
    private static final String PASSWORD = "123";
    private static final String BROKEURL = "tcp://localhost:61616";
    private static final int SENDNUM = 5; //发送消息的次数

    public static void createMQ() {
        ConnectionFactory connectionFactory;    //连接工厂
        Connection connection = null;  //连接
        Session session;    //会话,接收或发送消息的线程
        Destination destination;    //消息目的地
        MessageProducer messageProducer;    //消息生产者

        //实例化连接工厂
        connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEURL);
        try {
            connection = connectionFactory.createConnection(); //通过连接工厂获取连接
            connection.start(); //启动连接
            session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE); //创建session 参数:是否增加事物,消息确定方式
            destination = session.createQueue("HQ"); //创建消息队列
            messageProducer = session.createProducer(destination); //创建消息生产者
            for (int i = 1; i <= SENDNUM; i++) {
                String msg = MessageFormat.format("MQ消息:{0}", i);
                System.out.println("发送消息:" + msg);
                TextMessage message = session.createTextMessage(msg);
                messageProducer.send(message);
            }
            session.commit();
        } catch (JMSException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            if(connection != null){
                try {
                    connection.close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

消费者

package com.mq.p2p;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
 * 类  名:JMSConsumer
 * 类描述:点对点 - 消费者
 */
public class JMSConsumer {
    private static final String USERNAME = "hq";
    private static final String PASSWORD = "123";
    private static final String BROKEURL = "tcp://localhost:61616";

    public static void consumeMQ(){
        ConnectionFactory connectionFactory;    //连接工厂
        Connection connection = null;  //连接
        Session session;    //会话,接收或发送消息的线程
        Destination destination;    //消息目的地
        MessageConsumer messageConsumer;    //消息消费者

        //实例化连接工厂
        connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEURL);
        try {
            connection=connectionFactory.createConnection(); //通过连接工厂获取连接
            connection.start(); //启动连接
            session=connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE); //创建Session
            destination=session.createQueue("HQ"); //创建连接的消息队列
            messageConsumer=session.createConsumer(destination); //创建消息消费者
            messageConsumer.setMessageListener(new JMSListener()); // 注册消息监听
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

消息监听

package com.mq.p2p;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
/**
 * 类  名:JMSListener
 * 类描述:消息监听
 */
public class JMSListener implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            TextMessage textMessage = (TextMessage) message;
            System.out.println("接收到MQ消息:" + textMessage.getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

 

发布订阅(Pub/Sub

客户端将消息发送到主题。多个发布者将消息发送到 Topic,系统将这些消息传递给多个订阅者。如果你希望发送的消息可以不被做任何处理、或者被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用 Pub/Sub 模型。特点如下:
1.每个消息可以有多个消费者。
2.发布者和订阅者之间有时间上的依赖性。必须先订阅,才能消费,而且为了消费消息,订阅者必须保持运行的状态。
3.为了缓和这样严格的时间相关性,JMS 允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有运行,它也能接收到发布者的消息。

发布者

package com.mq.topic;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.text.MessageFormat;
/**
 * 发布订阅 - 发布者
 */
public class JMSProducer {
    private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
    private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
    private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;
    private static final int SENDNUM = 5; //发送消息的次数

    public static void createMQ() {
        ConnectionFactory connectionFactory;    //连接工厂
        Connection connection = null;  //连接
        Session session;    //会话,接收或发送消息的线程
        Destination destination;    //消息目的地
        MessageProducer messageProducer;    //消息生产者

        //实例化连接工厂
        connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEURL);
        try {
            connection = connectionFactory.createConnection(); //通过连接工厂获取连接
            connection.start(); //启动连接
            session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE); //创建session 参数:是否增加事物,消息确定方式
            destination = session.createTopic("HQ_topic"); //创建消息队列
            messageProducer = session.createProducer(destination); //创建消息生产者
            for (int i = 1; i <= SENDNUM; i++) {
                String msg = MessageFormat.format("MQ消息:{0}", i);
                System.out.println("Topic发送消息:" + msg);
                TextMessage message = session.createTextMessage(msg);
                messageProducer.send(message);
            }
            session.commit();
        } catch (JMSException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            if(connection != null){
                try {
                    connection.close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

订阅者

package com.mq.topic;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
 * 发布订阅 - 订阅者
 */
public class JMSConsumer {
    private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
    private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
    private static final String BROKEURL = ActiveMQConnection.DEFAULT_BROKER_URL;

    public static void consumeMQ(){
        ConnectionFactory connectionFactory;    //连接工厂
        Connection connection = null;  //连接
        Session session;    //会话,接收或发送消息的线程
        Destination destination;    //消息目的地
        MessageConsumer messageConsumer;    //消息消费者

        //实例化连接工厂
        connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKEURL);
        try {
            connection=connectionFactory.createConnection(); //通过连接工厂获取连接
            connection.start(); //启动连接
            session=connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE); //创建Session
            destination=session.createTopic("HQ_topic"); //创建连接的消息队列
            messageConsumer=session.createConsumer(destination); //创建消息消费者
            messageConsumer.setMessageListener(new JMSListener()); // 注册消息监听
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

消息监听

package com.mq.topic;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
/**
 * 发布订阅 - 消息监听
 */
public class JMSListener implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            TextMessage textMessage = (TextMessage) message;
            System.out.println("订阅者一接收到MQ消息:" + textMessage.getText());
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

 

ActiveMQ 与 Spring4.0 整合

pom.xml 文件配置

<!-- ActiveMQ -->
<dependency>
	<groupId>org.apache.activemq</groupId>
	<artifactId>activemq-broker</artifactId>
	<version>5.15.2</version>
</dependency>
<dependency>
	<groupId>org.apache.activemq</groupId>
	<artifactId>activemq-client</artifactId>
	<version>5.15.2</version>
</dependency>
<dependency>
	<groupId>org.apache.activemq</groupId>
	<artifactId>activemq-spring</artifactId>
	<version>5.15.2</version>
</dependency>

applicationContext.xml 文件配置

<!-- connectionFactory 是 Spring 用于创建到 JMS 服务器链接的 -->
<bean id="jmsFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
	<property name="brokerURL" value="tcp://localhost:61616"/>
	<property name="userName" value="hq"/>
	<property name="password" value="123"/>
</bean>
<!-- 配置生产者。利用 Spring 提供的 JmsTemplate 类可以配置消息发送 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
	<property name="connectionFactory" ref="jmsFactory"/>
	<!-- value="false":队列模式;value="true":发布订阅模式 -->
    <property name="pubSubDomain" value="false" />
	<property name="receiveTimeout" value="30000" />
</bean>
<!-- 目的地,即队列名称 -->
<bean id="hqQueue" class="org.apache.activemq.command.ActiveMQQueue">
	<constructor-arg index="0" value="hq"/>
</bean>
<!-- 监听器 -->
<bean id="hqListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
	<property name="delegate" ref="HqReceiver"/>
</bean>
<!-- 消息监听容器。每个消费者对应每个目的地都需要有对应的 MessageListenerContainer -->
<bean id="hqListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
	<property name="connectionFactory" ref="jmsFactory"/>
	<property name="messageListener" ref="hqListener"/>
	<property name="destination" ref="hqQueue"/>
	<!-- ack 消息确认机制。4:单条消息确认 -->
	<property name="sessionAcknowledgeMode" value="4"/>
</bean>

ack 消息确定机制

JMS 规范了四种 ack 消息确认机制。
1. AUTO_ACKNOWLEDGE = 1  自动确认
2. CLIENT_ACKNOWLEDGE = 2  客户端手动确认 
3. DUPS_OK_ACKNOWLEDGE = 3  自动批量确认
4. SESSION_TRANSACTED = 0  事务提交并确认

ActiveMQ 补充了一个自定义的 ack 模式:
INDIVIDUAL_ACKNOWLEDGE = 4  单条消息确认

ActiveMQ 与 Spring 整合时,如果想设置 ack 消息确认机制为客户端手动确认,在配置消费者监听器的时候,设置sessionAcknowledgeMode 的值为 2。但遗憾的是这种设置并不起作用,通过一段源码可以发现,当配置2时,会被 Spring 自动确认消费。所以,如果想设置 ack 消息确认机制为客户端手动确认,通常是将 sessionAcknowledgeMode 设置为4。在消费者的 onMessage 中,看可以通过 textMessage.acknowledge() 方法手动确认消息的消费。

//org.springframework.jms.listener.AbstractMessageListenerContainer 的一段源码
protected void commitIfNecessary(Session session, Message message) throws JMSException {
	if (session.getTransacted()) {
		if (isSessionLocallyTransacted(session)) {
			JmsUtils.commitIfNecessary(session);
		}
	}
	else if (message != null && isClientAcknowledge(session)) {
		message.acknowledge();
	}
}
protected boolean isClientAcknowledge(Session session) throws JMSException {
	//Spring 自动确认 CLIENT_ACKNOWLEDGE 的情况
	return (session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE);
}

生产者

import javax.annotation.Resource;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;

/**
* 生产者
*/
@Service
public class ProducerService {

    @Resource(name="jmsTemplate")
    private JmsTemplate jmsTemplate;
       
    /**
     * 向指定队列发送消息
     */
    public void sendMessage(Destination destination, final String msg) {
      System.out.println("向队列" + destination.toString() + "发送了消息------------" + msg);
      jmsTemplate.send(destination, new MessageCreator() {
        public Message createMessage(Session session) throws JMSException {
          return session.createTextMessage(msg);
        }
      });
    }
}

消费者

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.listener.SessionAwareMessageListener;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;
/**
 * 消费者
 */
@Component
public class HqReceiver implements SessionAwareMessageListener<TextMessage> {
    @Override
    public void onMessage(TextMessage textMessage, Session session) throws JMSException {
        String text = textMessage.getText();
		//todo sth.
		//手动消费
		textMessage.acknowledge();
    }
}

 

用户名密码

ActiveMQ 中包含两套用户系统,分别是应用服务系统、管理系统。

应用服务系统账户

1. ActiveMQ 默认不设置用户名密码,实例化连接工厂时直接使用系统默认值即可。
2. 设置 ActiveMQ 的访问用户名和密码,可以提高系统的安全性。账号信息在 conf/activemq.xml 文件的 <broker> 节点中添加。如此,实例化连接工厂时如果用户名密码不正确便会报运行时异常。

<!-- 账号 & 密码 -->
<plugins>  
    <simpleAuthenticationPlugin>  
        <users>  
            <authenticationUser username="hq" password="123" groups="users,admins"/>  
        </users>  
    </simpleAuthenticationPlugin>  
</plugins> 

管理系统

管理系统的账号默认是 admin/admin,但可以根据需要重新编辑账户信息。

1. 设置免登陆:ActiveMQ 管理系统是基于 jetty 服务器运行的,将 conf/jetty.xml 文件中的 id="securityConstraint" 的节点的 name="authenticate" 属性的值设置为 false 即可。 

2. 设置用户名密码:这是用户名密码管理系统的用户名密码保存在 conf/jetty-realm.properties 文件中。格式:用户名 : 密码,角色名。

 

转载于:https://my.oschina.net/u/2563695/blog/1601588

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值