ActiveMQ入门(一)

什么是ActiveMQ

ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。

主要特点:

1. 多种语言和协议编写客户端。语言: Java, C, C++, C#, Ruby, Perl, Python, PHP。应用协议: OpenWire,Stomp REST,WS Notification,XMPP,AMQP

2. 完全支持JMS1.1和J2EE 1.4规范 (持久化,XA消息,事务)

3. 对Spring的支持,ActiveMQ可以很容易内嵌到使用Spring的系统里面去,而且也支持Spring2.0的特性

4. 通过了常见J2EE服务器(如 Geronimo,JBoss 4, GlassFish,WebLogic)的测试,其中通过JCA 1.5 resource adaptors的配置,可以让ActiveMQ可以自动的部署到任何兼容J2EE 1.4 商业服务器上

5. 支持多种传送协议:in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA

6. 支持通过JDBC和journal提供高速的消息持久化

7. 从设计上保证了高性能的集群,客户端-服务器,点对点

8. 支持Ajax

9. 支持与Axis的整合

10. 可以很容易得调用内嵌JMS provider,进行测试

JMS介绍

JMS的全称是Java Message Service,即Java消息服务。用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。

它主要用于在生产者和消费者之间进行消息传递,生产者负责产生消息,而消费者负责接收消息。把它应用到实际的业务需求中的话我们可以在特定的时候利用生产者生成一消息,并进行发送,对应的消费者在接收到对应的消息后去完成对应的业务逻辑。

对于消息的传递有两种类型:

一种是点对点的,即一个生产者和一个消费者一一对应;

另一种是发布/订阅模式,即一个生产者产生消息并进行发送后,可以由多个消费者进行接收。

JMS定义了五种不同的消息正文格式,以及调用的消息类型,允许你发送并接收以一些不同形式的数据,提供现有消息格式的一些级别的兼容性。

  · StreamMessage -- Java原始值的数据流

  · MapMessage--一套名称-值对

  · TextMessage--一个字符串对象

  · ObjectMessage--一个序列化的 Java对象

  · BytesMessage--一个字节的数据流

ActiveMQ的安装

   下载

进入http://activemq.apache.org/下载ActiveMQ

 

安装

安装步骤:

第一步:安装jdk,需要jdk1.7以上版本

第二步:解压缩activeMQ的压缩包。

第三步:进入bin目录。

启动:[root@localhost bin]# ./activemq start

停止:[root@localhost bin]# ./activemq stop

windows进入控制台切换到解压目录下执行activemq start

第四步:访问后台管理。

http://localhost:8161/admin

用户名:admin

密码:admin

ActiveMQ的使用方法

JMS消息发送模式

在点对点或队列模型下,一个生产者向一个特定的队列发布消息,一个消费者从该队列中读取消息。这里,生产者知道消费者的队列,并直接将消息发送到消费者的队列。这种模式被概括为:只有一个消费者将获得消息。生产者不需要在接收者消费该消息期间处于运行状态,接收者也同样不需要在消息发送时处于运行状态。每一个成功处理的消息都由接收者签收。

发布者/订阅者模型支持向一个特定的消息主题发布消息。0或多个订阅者可能对接收来自特定消息主题的消息感兴趣。在这种模型下,发布者和订阅者彼此不知道对方。这种模式好比是匿名公告板。这种模式被概括为:多个消费者可以获得消息.在发布者和订阅者之间存在时间依赖性。发布者需要建立一个订阅(subscription),以便客户能够购订阅。订阅者必须保持持续的活动状态以接收消息,除非订阅者建立了持久的订阅。在那种情况下,在订阅者未连接时发布的消息将在订阅者重新连接时重新发布。

JMS应用程序接口

ConnectionFactory 接口(连接工厂)

用户用来创建到JMS提供者的连接的被管对象。JMS客户通过可移植的接口访问连接,这样当下层的实现改变时,代码不需要进行修改。 管理员在JNDI名字空间中配置连接工厂,这样,JMS客户才能够查找到它们。根据消息类型的不同,用户将使用队列连接工厂,或者主题连接工厂。

Connection 接口(连接)

连接代表了应用程序和消息服务器之间的通信链路。在获得了连接工厂后,就可以创建一个与JMS提供者的连接。根据不同的连接类型,连接允许用户创建会话,以发送和接收队列和主题到目标。

Destination 接口(目标)

目标是一个包装了消息目标标识符的被管对象,消息目标是指消息发布和接收的地点,或者是队列,或者是主题。JMS管理员创建这些对象,然后用户通过JNDI发现它们。和连接工厂一样,管理员可以创建两种类型的目标,点对点模型的队列,以及发布者/订阅者模型的主题。

MessageConsumer 接口(消息消费者)

由会话创建的对象,用于接收发送到目标的消息。消费者可以同步地(阻塞模式),或异步(非阻塞)接收队列和主题类型的消息。

MessageProducer 接口(消息生产者)

由会话创建的对象,用于发送消息到目标。用户可以创建某个目标的发送者,也可以创建一个通用的发送者,在发送消息时指定目标。

Message 接口(消息)

是在消费者和生产者之间传送的对象,也就是说从一个应用程序创送到另一个应用程序。一个消息有三个主要部分:

消息头(必须):包含用于识别和为消息寻找路由的操作设置。

一组消息属性(可选):包含额外的属性,支持其他提供者和用户的兼容。可以创建定制的字段和过滤器(消息选择器)。

一个消息体(可选):允许用户创建五种类型的消息(文本消息,映射消息,字节消息,流消息和对象消息)。

消息接口非常灵活,并提供了许多方式来定制消息的内容。

Session 接口(会话)

表示一个单线程的上下文,用于发送和接收消息。由于会话是单线程的,所以消息是连续的,就是说消息是按照发送的顺序一个一个接收的。会话的好处是它支持事务。如果用户选择了事务支持,会话上下文将保存一组消息,直到事务被提交才发送这些消息。在提交事务之前,用户可以使用回滚操作取消这些消息。一个会话允许用户创建消息生产者来发送消息,创建消息消费者来接收消息。

入门代码

生产者消费者模型

package com.hj.ActivityMQ;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.omg.CORBA.ACTIVITY_COMPLETED;

/**
 * 
 * @author 生产者
 *
 */
public class Producer {

	// 默认连接用户名
	private static final String USERNAME = "admin";
	// 默认连接密码
	private static final String PASSWORD = "admin";
	// 默认连接地址
	private static final String BROKER_URL = "tcp://localhost:61616";

	public static void main(String[] args) {
		// 连接工厂
		ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKER_URL);

		try {
			// 连接
			Connection connection = connectionFactory.createConnection();
			// 启动连接
			connection.start();
			// 创建session
			/*
			 * connection.createSession(paramA, paramB) A)paramA设置为true时: paramB的值忽略,
			 * acknowledgment mode被jms服务器设置 SESSION_TRANSACTED 。 当一个事务被提交的时候,消息确认就会自动发生。 B)
			 * paramA设置为false时: Session.AUTO_ACKNOWLEDGE为自动确认,当客户成功的从receive方法返回的时候,或者从
			 * MessageListener.onMessage方法成功返回的时候,会话自动确认客户收到的消息。 Session.CLIENT_ACKNOWLEDGE
			 * 为客户端确认。客户端接收到消息后,必须调用javax.jms.Message的 acknowledge方法。jms服务器才会删除消息。(默认是批量确认)
			 */

			Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
			// 消息目的地
			Destination destination = session.createQueue("FirstQueue");
			// 消息生产者
			MessageProducer producer = session.createProducer(destination);
			// 设置不持久化,此处学习,实际根据项目决定
			producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

			// 发送消息
			for (int i = 0; i < 5; i++) {
				// 创建一条文本消息
				TextMessage message = session.createTextMessage("ActiveMQ: 这是第 " + i + " 条消息");
				System.out.println("send----->ActiveMQ: 这是第 " + i + " 条消息");
				// 生产者发送消息
				producer.send(message);
			}
			session.close();
			connection.close();
		} catch (JMSException e) {
			e.printStackTrace();
		}
	}
}
package com.hj.ActivityMQ;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * 
 * @author 消费者
 *
 */
public class Consumer {

	// 默认连接用户名
	private static final String USERNAME = "admin";
	// 默认连接密码
	private static final String PASSWORD = "admin";
	// 默认连接地址
	private static final String BROKER_URL = "tcp://localhost:61616";

	public static void main(String[] args) {
		// 连接工厂
		ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKER_URL);
		try {
			// 连接
			Connection connection = connectionFactory.createConnection();
			// 启动连接
			connection.start();
			// 创建session
			Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
			// 消息目的地
			Destination destination = session.createQueue("FirstQueue");
			// 消息消费者
			MessageConsumer consumer = session.createConsumer(destination);
			while (true) {
				TextMessage message = (TextMessage) consumer.receive();
				if (message != null) {
					System.out.println("接收到消息: " + message.getText());
				} else {
					break;
				}
			}
			session.close();
			connection.close();
		} catch (JMSException e) {
			e.printStackTrace();
		}
	}
}

结果:

 

 可以看到生产消费者模型是异步的。

订阅者模式

package com.hj.ActivityMQPublisher;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQTopic;

import javax.jms.*;

class Publisher {
	// 默认连接用户名
	private static final String USERNAME = "admin";
	// 默认连接密码
	private static final String PASSWORD = "admin";
	// 默认连接地址
	private static final String BROKER_URL = "tcp://localhost:61616";

	public static void main(String[] args) throws JMSException {

		String destination = arg(args, 0, "event");
		int messages = 10000;
		int size = 256;
		String DATA = "abcdefghijklmnopqrstuvwxyz";
		String body = "";
		for (int i = 0; i < size; i++) {
			body += DATA.charAt(i % DATA.length());
		}

		// 连接工厂
		ConnectionFactory factory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKER_URL);
		Connection connection = factory.createConnection();
		connection.start();
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		Destination dest = new ActiveMQTopic(destination);
		MessageProducer producer = session.createProducer(dest);
		producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
		for (int i = 1; i <= messages; i++) {
			TextMessage msg = session.createTextMessage(body);
			msg.setIntProperty("id", i);
			producer.send(msg);
			if ((i % 1000) == 0) {
				System.out.println(String.format("Sent %d messages", i));
			}
		}

		producer.send(session.createTextMessage("SHUTDOWN"));
		connection.close();

	}

	private static String arg(String[] args, int index, String defaultValue) {
		if (index < args.length)
			return args[index];
		else
			return defaultValue;
	}

}
package com.hj.ActivityMQPublisher;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQTopic;

import javax.jms.*;

class Listener {
	// 默认连接用户名
		private static final String USERNAME = "admin";
		// 默认连接密码
		private static final String PASSWORD = "admin";
		// 默认连接地址
		private static final String BROKER_URL = "tcp://localhost:61616";
	public static void main(String[] args) {
		String destination = arg(args, 0, "event");

		// 连接工厂
		ConnectionFactory factory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKER_URL);

		Connection connection = null;
		try {
			connection = factory.createConnection();
			connection.start();
			Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
			Destination dest = new ActiveMQTopic(destination);
			MessageConsumer consumer = session.createConsumer(dest);
			long start = System.currentTimeMillis();
			long count = 1;
			System.out.println("Waiting for messages...");
			while (true) {
				Message msg = consumer.receive();
				if (msg instanceof TextMessage) {
					String body = ((TextMessage) msg).getText();
					if ("SHUTDOWN".equals(body)) {
						long diff = System.currentTimeMillis() - start;
						System.out.println(String.format("Received %d in %.2f seconds", count, (1.0 * diff / 1000.0)));
						//break;
					} else {
						if (count != msg.getIntProperty("id")) {
							System.out.println("mismatch: " + count + "!=" + msg.getIntProperty("id"));
						}
						count = msg.getIntProperty("id");

						if (count == 0) {
							start = System.currentTimeMillis();
						}
						if (count % 1000 == 0) {
							System.out.println(String.format("Received %d messages.", count));
						}
						count++;
					}

				} else {
					System.out.println("Unexpected message type: " + msg.getClass());
				}
			}
		} catch (JMSException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				connection.close();
			} catch (JMSException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	private static String arg(String[] args, int index, String defaultValue) {
		if (index < args.length)
			return args[index];
		else
			return defaultValue;
	}
}

结果:

 

 

订阅者模式是同步的。 

记一个踩过的坑

在配置activitymq是没有注意,一开始使用了默认用户名和密码,导致报错Exception in thread "main" javax.jms.JMSSecurityException: User name [null] or password is invalid.。查了好久是/conf/activitymq.xml配置问题,参考文章activitymq配置,我把配置文件附上吧!!省得大家改了。复制就行了。

<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
	license agreements. See the NOTICE file distributed with this work for additional 
	information regarding copyright ownership. The ASF licenses this file to 
	You under the Apache License, Version 2.0 (the "License"); you may not use 
	this file except in compliance with the License. You may obtain a copy of 
	the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required 
	by applicable law or agreed to in writing, software distributed under the 
	License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 
	OF ANY KIND, either express or implied. See the License for the specific 
	language governing permissions and limitations under the License. -->
<!-- START SNIPPET: example -->
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">

	<!-- Allows us to use system properties as variables in this configuration 
		file -->
	<bean
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<value>file:${activemq.conf}/credentials.properties</value>
		</property>
	</bean>

	<!-- Allows accessing the server log -->
	<bean id="logQuery"
		class="io.fabric8.insight.log.log4j.Log4jLogQuery" lazy-init="false"
		scope="singleton" init-method="start" destroy-method="stop">
	</bean>

	<!-- The <broker> element is used to configure the ActiveMQ broker. -->
	<broker xmlns="http://activemq.apache.org/schema/core"
		brokerName="localhost" dataDirectory="${activemq.data}">

		<destinationPolicy>
			<policyMap>
				<policyEntries>
					<policyEntry topic=">">
						<!-- The constantPendingMessageLimitStrategy is used to prevent slow 
							topic consumers to block producers and affect other consumers by limiting 
							the number of messages that are retained For more information, see: http://activemq.apache.org/slow-consumer-handling.html -->
						<pendingMessageLimitStrategy>
							<constantPendingMessageLimitStrategy
								limit="1000" />
						</pendingMessageLimitStrategy>
					</policyEntry>
				</policyEntries>
			</policyMap>
		</destinationPolicy>


		<!-- The managementContext is used to configure how ActiveMQ is exposed 
			in JMX. By default, ActiveMQ uses the MBean server that is started by the 
			JVM. For more information, see: http://activemq.apache.org/jmx.html -->
		<managementContext>
			<managementContext createConnector="false" />
		</managementContext>

		<!-- Configure message persistence for the broker. The default persistence 
			mechanism is the KahaDB store (identified by the kahaDB tag). For more information, 
			see: http://activemq.apache.org/persistence.html -->
		<persistenceAdapter>
			<kahaDB directory="${activemq.data}/kahadb" />
		</persistenceAdapter>


		<!-- The systemUsage controls the maximum amount of space the broker will 
			use before disabling caching and/or slowing down producers. For more information, 
			see: http://activemq.apache.org/producer-flow-control.html -->
		<systemUsage>
			<systemUsage>
				<memoryUsage>
					<memoryUsage percentOfJvmHeap="70" />
				</memoryUsage>
				<storeUsage>
					<storeUsage limit="100mb" />
				</storeUsage>
				<tempUsage>
					<tempUsage limit="50mb" />
				</tempUsage>
			</systemUsage>
		</systemUsage>

		<!-- The transport connectors expose ActiveMQ over a given protocol to 
			clients and other brokers. For more information, see: http://activemq.apache.org/configuring-transports.html -->
		<transportConnectors>
			<!-- DOS protection, limit concurrent connections to 1000 and frame size 
				to 100MB -->
			<transportConnector name="openwire"
				uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600" />
			<transportConnector name="amqp"
				uri="amqp://0.0.0.0:5672?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600" />
			<transportConnector name="stomp"
				uri="stomp://0.0.0.0:61613?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600" />
			<transportConnector name="mqtt"
				uri="mqtt://0.0.0.0:1883?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600" />
			<transportConnector name="ws"
				uri="ws://0.0.0.0:61614?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600" />
		</transportConnectors>

		<!-- destroy the spring context on shutdown to stop jetty -->
		<shutdownHooks>
			<bean xmlns="http://www.springframework.org/schema/beans"
				class="org.apache.activemq.hooks.SpringContextHook" />
		</shutdownHooks>

		<plugins>
			<simpleAuthenticationPlugin>
				<users>
					<authenticationUser username="admin"
						password="admin" groups="users,admins" />
				</users>
			</simpleAuthenticationPlugin>
		</plugins>
	</broker>

	<!-- Enable web consoles, REST and Ajax APIs and demos The web consoles 
		requires by default login, you can disable this in the jetty.xml file Take 
		a look at ${ACTIVEMQ_HOME}/conf/jetty.xml for more details -->
	<import resource="jetty.xml" />

</beans>
<!-- END SNIPPET: example -->

猜你喜欢

转载自blog.csdn.net/weixin_40657079/article/details/84440488