ActiveMQ消息队列
ActiveMQ消息队列
勤劳的开发者px 发表于3个月前
ActiveMQ消息队列
  • 发表于 3个月前
  • 阅读 12
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 新注册用户 域名抢购1元起>>>   

1、JMS

JMS是java的消息服务,即Java Message Service,分为消息生产者(Producer)与消息消费者(consumer),JMS具体实现依赖于消息中间件。

2、消息中间件

消息中间件(MOM:Message Orient middleware)。

消息中间件有很多的用途和优点: 
1. 将数据从一个应用程序传送到另一个应用程序,或者从软件的一个模块传送到另外一个模块; 
2. 负责建立网络通信的通道,进行数据的可靠传送。 
3. 保证数据不重发,不丢失 
4. 能够实现跨平台操作,能够为不同操作系统上的软件集成技工数据传送服务

  • 消息的接受和转发的容器

3、ActiveMQ

下载ActiveMQ

官方网站:http://activemq.apache.org/ 

这里写图片描述

安装目录:

  • bin存放的是脚本文件
  • conf存放的是基本配置文件
  • data存放的是日志文件
  • docs存放的是说明文档
  • examples存放的是简单的实例
  • lib存放的是activemq所需jar包
  • webapps用于存放项目的目录

ActiveMQ 启动

双击bin目录下的activemq.bat脚本文件或运行自己电脑版本下的activemq.bat,从图中可以看到activemq的存放地址,以及浏览器要访问的地址. 。 
这里写图片描述

 ActiveMQ 测试

ActiveMQ默认使用的TCP连接端口是61616, 通过查看该端口的信息可以测试ActiveMQ是否成功启动 netstat -an|find “61616”

C:\Documents and Settings\Administrator>netstat -an|find "61616" 
TCP     0.0.0.0:61616     0.0.0.0:0       LISTENING

ActiveMQ 监测

ActiveMQ默认启动时,启动了内置的jetty服务器,提供一个用于监控ActiveMQ的admin应用。 
admin:http://127.0.0.1:8161/admin/

用户名和密码都是admin

这里写图片描述

停止服务器,只需要按着Ctrl+Shift+C,之后输入y即可。

4、消息模型

点对点/一对一:

  1. 消息队列(Queue)
  2. 发送者(Sender)
  3. 接收者(Receiver)
  • 每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。

多对多/发布订阅模式

  1. 主题(Topic)
  2. 发布者(Publisher)
  3. 订阅者(Subscriber) 
  • 客户端将消息发送到主题。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者

消息的消费 
在JMS中,消息的产生和消息是异步的。对于消费来说,JMS的消息者可以通过两种方式来消费消息。 

  • 同步 :订阅者或接收者调用receive方法来接收消息,receive方法在能够接收到消息之前(或超时之前)将一直阻塞 
  • 异步 :订阅者或接收者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的onMessage方法。

5、 JMS编程涉及的主要对象

ConnectionFactory:创建Connection对象的工厂,分QueueConnectionFactory和TopicConnectionFactory两种。

Connection:Connection表示在客户端和JMS系统之间建立的链接(对TCP/IP socket的包装)。Connection可以产生一个或多个Session。

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

Destination:消息发送目标或消息来源。对于消息生产者来说,其Destination是某个队列(Queue)或某个主题(Topic);对于消息消费者来说,其Destination也是某个队列或主题(即消息来源)。

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

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

MessageListener:消息监听器。如果注册了消息监听器,一旦消息到达,将自动调用监听器的onMessage方法。EJB中的MDB(Message-Driven Bean)就是一种MessageListener。

6、demo

发送消息

    public static void main(String[] args) {
        //实例化连接工厂
        ConnectionFactory  connectionFactory = new   
        ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, 
        ActiveMQConnection.DEFAULT_PASSWORD, ActiveMQConnection.DEFAULT_BROKER_URL);
        try {
            //通过连接工厂获取连接
            Connection  connection = connectionFactory.createConnection();
            //启动连接
            connection.start();
            //创建session
            Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
            //创建一个名称为HelloWorld的消息队列
            Destination destination = session.createQueue("HelloWorld");
            //创建消息生产者
            MessageProducer  messageProducer = session.createProducer(destination);
            //设置不持久化,此处学习,实际根据项目决定
            messageProducer.setDeliveryMode(DeliveryMode. PERSISTENT);
            //发送消息
            sendMessage(session, messageProducer);
            session.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            if(connection != null){
                try {
                    connection.close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * 发送消息
     * @param session
     * @param messageProducer  消息生产者
     * @throws Exception
     */
    public static void sendMessage(Session session,MessageProducer messageProducer) throws Exception{
        for (int i = 0; i < 10; i++) {
            //创建一条文本消息 
            TextMessage message = session.createTextMessage("发送消息" +i);
            System.out.println("发送消息: 发送消息" + i);
            //通过消息生产者发出消息 
            messageProducer.send(message);
        }

    }
​

接受消息 

 public static void main(String[] args) {
        //实例化连接工厂
       ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, 
        ActiveMQConnection.DEFAULT_PASSWORD, ActiveMQConnection.DEFAULT_BROKER_URL);
        try {
            //通过连接工厂获取连接
            Connection  connection = connectionFactory.createConnection();
            //启动连接
            connection.start();
            //创建session
            Session  session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            //创建一个连接HelloWorld的消息队列
            Destination  destination = session.createQueue("HelloWorld");
            //创建消息消费者
            messageConsumer = session.createConsumer(destination);

            while (true) {
                TextMessage textMessage = (TextMessage) messageConsumer.receive(100000);
                if(textMessage != null){
                    System.out.println("收到的消息:" + textMessage.getText());
                }else {
                    break;
                }
            }

        } catch (JMSException e) {
            e.printStackTrace();
        }

    }
}

运行ActiveMQ服务器,能查看消息生产与消费情况

这里写图片描述

7、Spring和ActiveMQ整合

    <!-- ActiveMQ 连接工厂 -->
    <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->
    <!-- 如果连接网络:tcp://ip:61616;未连接网络:tcp://localhost:61616 以及用户名,密码-->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="failover:tcp://127.0.0.1:61616" />
    <!-- 消息传输监听器 处理网络及服务器异常 -->
    <property name="transportListener">
        <bean class="cn.mayongfa.activemq.ActiveMQTransportListener" />
    </property>
</bean>

    <!-- Spring Caching连接工厂 -->
    <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
    <bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
        <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
        <property name="targetConnectionFactory" ref="amqConnectionFactory"></property>
        <!-- 同上,同理 -->
        <!-- <constructor-arg ref="amqConnectionFactory" /> -->
        <!-- Session缓存数量 -->
        <property name="sessionCacheSize" value="100" />
    </bean>

    <!-- Spring JmsTemplate 的消息生产者 start-->

    <!-- 定义JmsTemplate的Queue类型 -->
    <bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
        <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->  
        <constructor-arg ref="connectionFactory" />
        <!-- 非pub/sub模型(发布/订阅),即队列模式 -->
        <property name="pubSubDomain" value="false" />
    </bean>

    <!-- 定义JmsTemplate的Topic类型 -->
    <bean id="jmsTopicTemplate" class="org.springframework.jms.core.JmsTemplate">
         <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->  
        <constructor-arg ref="connectionFactory" />
        <!-- pub/sub模型(发布/订阅) -->
        <property name="pubSubDomain" value="true" />
    </bean>

    <!--这个是队列目的地 -->
    <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg>
             <value>default_queue,gold_queue</value>
        </constructor-arg>
    </bean>
    <!-- 消息监听器 -->
    <bean id="consumerMessageListener" class="cn.mayongfa.activemq.ConsumerMessageListener" />
    <!-- 消息监听容器 -->
    <bean id="jmsContainer"    
          class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="destination" ref="queueDestination" />
        <property name="messageListener" ref="consumerMessageListener" />
        <!-- 设置固定的线程数 -->
        <property name="concurrentConsumers" value="2"></property>
        <!-- 设置动态的线程数 -->
        <property name="concurrency" value="2-5"></property>
    </bean>

</beans>  

消息生产类

/**
 * ActiveMQ 消息生产类
 * 
 * @author Mafly
 *
 */
@Component
public class MessageSender {

private Logger log = Logger.getLogger(MessageSender.class);

@Autowired
private JmsTemplate jmsTemplate;

private String Queue = "default_queue";

private String GoldQueue = "gold_queue";

private Gson gson = new Gson();

/**
 * 用户登录消息
 */
public void userLogin(long id, String username) {
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("userid", id);
    map.put("username", username);

    System.out.println("发送了一条消息。");
    // 发送到金币队列
    sendMessage(gson.toJson(map), 1);
}
/**
 * 发送到消息队列
 * 
 * @param messgae
 * @param type
 *            类型,0:默认队列 1:金币队列 ...
 */
public void sendMessage(final String messgae, int type) {
    try {
        String destination = this.Queue;
        if (type == 1) {
            destination = GoldQueue;
        }
        jmsTemplate.send(destination, new MessageCreator() {
            @Override
            public Message createMessage(Session session) throws JMSException {
                TextMessage textMessage = session.createTextMessage(messgae);
                return textMessage;
            }
        });
    } catch (Exception e) {
        log.error("", e);
    }
}
}

消息消费类

/**
 * 消费者监听类
 * 
 * @author Mafly
 */
@Component
public class ConsumerMessageListener implements MessageListener {

private Logger log = Logger.getLogger(ConsumerMessageListener.class);

@Override
public void onMessage(Message arg0) {
    // 监听发送到消息队列的文本消息,作强制转换。
    TextMessage textMessage = (TextMessage) arg0;
    try {
        System.out.println("接收到的消息内容是:" + textMessage.getText());

        // TODO: 你喜欢的任何事情...

    } catch (JMSException e) {
        log.error("", e);
    }

}

}

消息传输监听类

/**
 * 消息传输监听
 * @author Mafly
 *
 */
public class ActiveMQTransportListener implements TransportListener {

private Logger log = Logger.getLogger(ActiveMQTransportListener.class);

/**     
 * 对消息传输命令进行监控     
 * @param command     
 */     
@Override     
public void onCommand(Object o) {
    
}     
 
/**     
 * 对监控到的异常进行触发     
 * @param error     
 */     
@Override     
public void onException(IOException error) {             
    log.error("onException -> 消息服务器连接错误......", error);
}     
 
/**     
 * 当failover时触发     
 */     
@Override     
public void transportInterupted() {     
    log.warn("transportInterupted -> 消息服务器连接发生中断...");     
    //这里就可以状态进行标识了
    
}     
 
/**     
 * 监控到failover恢复后进行触发     
 */     
@Override     
public void transportResumed() {     
    log.info("transportResumed -> 消息服务器连接已恢复..."); 
    //这里就可以进行状态标识了
}
}

8 ActiveMQ持久订阅设置

ActiveMQ支持两种传输模式:持久传输和非持久传输(persistent and non-persistent delivery),默认情况下使用的是持久传输。

持久传输和非持久传输最大的区别是:采用持久传输时,传输的消息会保存到磁盘中(messages are persisted to disk/database),即“存储转发”方式。先把消息存储到磁盘中,然后再将消息“转发”给订阅者。

采用非持久传输时,发送的消息不会存储到磁盘中。

采用持久传输时,当Borker宕机 恢复后,消息还在。采用非持久传输,Borker宕机重启后,消息丢失。比如,当生产者将消息投递给Broker后,Broker将该消息存储到磁盘中,在Broker将消息发送给Subscriber之前,Broker宕机了,如果采用持久传输,Broker重启后,从磁盘中读出消息再传递给Subscriber;如果采用非持久传输,这条消息就丢失了。

先设置消息的持久

//消息持久化
producer.setDeliveryMode(DeliveryMode.PERSISTENT);

订阅者设置持久 

public static void main(String[] args) {
		Connection connection = null;
		try {
			ActiveMQConnectionFactory connectionFactxory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, ActiveMQConnection.DEFAULT_BROKER_URL);
			//创建连接
			connection = connectionFactxory.createConnection();
			//设置持久id
			connection.setClientID("aa");
			connection.start();
			//获取session
			Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
			//创建消息主题
			Topic topic = session.createTopic("msg_4_publish");
			//创建普通消息消费者
//			MessageConsumer consumer = session.createConsumer(topic);
			//创建持久消息消费者
			TopicSubscriber subscriber = session.createDurableSubscriber(topic, "aa");
			while (true) {
//				TextMessage message = (TextMessage)consumer.receive();
				TextMessage message = (TextMessage)subscriber.receive();
				if(message!=null){
					System.out.println("subscriber_1   收到消息:"+message.getText());
				}else {
					break;
				}
				
			}
			
		} catch (JMSException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

9 与spring整合的持久化

消息生产者配置文件

 <!--第三方工厂 -->  
    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
        <property name="brokerURL" value="tcp://127.0.0.1:61616" />  
        <property name="userName" value="admin"></property>  
        <property name="password" value="admin"></property>  
        <property name="useAsyncSend" value="true" />  
    </bean>  
    <!-- ActiveMQ为我们提供了一个PooledConnectionFactory,通过往里面注入一个ActiveMQConnectionFactory   
        可以用来将Connection、Session和MessageProducer池化,这样可以大大的减少我们的资源消耗,要依赖于 activemq-pool包 -->  
    <bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">  
        <property name="connectionFactory" ref="targetConnectionFactory" />  
        <property name="maxConnections" value="100" />  
    </bean>  
  
    <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
    <bean id="connectionFactory"  
        class="org.springframework.jms.connection.SingleConnectionFactory">  
        <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
        <property name="targetConnectionFactory" ref="pooledConnectionFactory" />  
          <!--消费者标示id -->  
        <property name="clientId" value="clientId_001" />
    </bean>  
  
    <!-- topic目的地配置,其实不管是topic还是queue则他们的底层实现不同但是通过封装api就差不多了,而在spring中更是简单 -->  
    <bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">  
        <constructor-arg index="0" value="spring-topic" />  
    </bean>  
  
  
    <!-- spring 使用jmsTemplate来实现消息的发送和接受 -->  
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
        <property name="connectionFactory" ref="connectionFactory"></property>  
        <property name="defaultDestination" ref="destinationTopic"></property>  
        <!-- 进行持久化 -->  
        <property name="deliveryMode" value="2" />  
        <!-- 开启订阅模式 -->  
        <property name="pubSubDomain" value="true" />  
    </bean>  
</beans>  
//消息生产者
 public static void main(String[] args) {  
        ApplicationContext ctx = new ClassPathXmlApplicationContext(  
                "config/xxxx.xml");  
        // 获取JmsTemplate对象  
        jt = (JmsTemplate) ctx.getBean("jmsTemplate");  
        // 调用方法,发送消息  
        jt.send(new MessageCreator() {  
            // 消息的产生,返回消息发送消息  
            public Message createMessage(Session s) throws JMSException {  
                TextMessage msg = s  
                        .createTextMessage("Spring send msg ----> Hello activeMQ");  
                return msg;  
            }  
        });  
        System.out.println("end!");  
    }  

消费者配置文件

 <!--第三方工厂 -->  
    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
        <property name="brokerURL" value="tcp://127.0.0.1:61616" />  
        <property name="userName" value="admin"></property>  
        <property name="password" value="admin"></property>  
        <property name="useAsyncSend" value="true" />  
    </bean>  
    <!-- ActiveMQ为我们提供了一个PooledConnectionFactory,通过往里面注入一个ActiveMQConnectionFactory   
        可以用来将Connection、Session和MessageProducer池化,这样可以大大的减少我们的资源消耗,要依赖于 activemq-pool包 -->  
    <bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">  
        <property name="connectionFactory" ref="targetConnectionFactory" />  
        <property name="maxConnections" value="100" />  
    </bean>  
  
    <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
    <bean id="connectionFactory"  
        class="org.springframework.jms.connection.SingleConnectionFactory">  
        <!--消费者标示id -->  
        <property name="clientId" value="clientId_001" />  
        <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
        <property name="targetConnectionFactory" ref="pooledConnectionFactory" />  
    </bean>  
  
  
    <!-- topic目的地配置,其实不管是topic还是queue则他们的底层实现不同但是通过封装api就差不多了,而在spring中更是简单 -->  
    <bean id="destinationTopic" class="org.apache.activemq.command.ActiveMQTopic">  
        <constructor-arg index="0" value="spring-topic" />  
    </bean>  
  
    <!--消息消费者监听类 -->  
    <bean id="myMessageListener" class="springs.activemq.Service.MyMessageListener" />  
    <!--监听容器的配置 -->  
    <bean id="myListenerContainer"  
        class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
        <property name="connectionFactory" ref="connectionFactory" />  
        <!--消息目的地 -->  
        <property name="destination" ref="destinationTopic" />  
        <!--消息监听类 -->  
        <property name="messageListener" ref="myMessageListener" />  
        <!-- 发布订阅模式 -->  
        <property name="pubSubDomain" value="true" />  
        <!-- 消息持久化值设置为true -->  
        <property name="subscriptionDurable" value="true" />  
        <!--消息接收超时 -->  
        <property name="receiveTimeout" value="10000" />  
        <!-- 接收者ID -->  
        <property name="clientId" value="clientId_001" />  
        <property name="durableSubscriptionName" value="clientId_001" />  
    </bean>  
</beans>  
//消息监听类
public class MyMessageListener implements MessageListener {  
    public void onMessage(Message arg0) {  
        // TODO Auto-generated method stub  
        try {  
            String message = ((TextMessage) arg0).getText();  
            System.out.println("textmessage:" + message);  
        } catch (JMSException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
  
}

三种消息监听参考:http://dwj147258.iteye.com/blog/2330295

共有 人打赏支持
粉丝 2
博文 48
码字总数 99832
评论 (0)
×
勤劳的开发者px
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: