activeMQ初步搭建及使用

1.下载及安装

下载地址:http://activemq.apache.org/activemq-590-release.html

下载完成后,解压,并运行bin目录下自己系统对应的activemq:

运行出现以下界面说明启动成功:

访问http://localhost:8161/进入后台管理界面:

初始用户名和密码都是admin:

进入之后看到如下界面就可以查看连接信息

2.MQ应用场景:异步处理、应用解耦、流量削峰等。

2.1引入异步处理的情景

在我们没有使用消息中间件时,例如用户在注册信息时,需要执行的流程是:注册信息写入数据库(假设耗时50ms)——发送注册邮件(50ms)——发送注册短信(50ms),那么用户收到响应总共耗时150ms。而在我们使用消息队列时,我们通过异步处理的方式,在用户注册信息将信息写入到数据库后(50ms),我们可以在消息队列中写入一条消息(写入消息的速度较快,假设耗时5ms),然后异步处理发送注册邮件和发送注册短信,这两个行为是从消息队列中异步读取信息进行处理的,在消息队列中写入消息之后就可以给客户响应了,不需要等待后面发送邮件及短信的处理过程,所以这种情况下响应耗时55ms,缩短了用户等待时间。

2.2引入应用解耦的场景

例如在订单系统和库存系统中,传统的做法是用户下订单之后,调用库存系统的接口对库存进行相应的减量,这样耦合度就较高。这种方式的弊端是假如库存系统出现了问题,就会导致订单系统调用失败,用户下单失败。

在加入消息中间件后,订单系统不需要直接调用库存系统(降低了耦合),而是订单系统在消息队列中发送一条消息,然后库存系统来订阅消息进而对库存减量,这样在用户下了订单之后,不用关心库存系统的情况就可以直接响应客户。

2.3引入流量削峰的情景

一般用于秒杀活动中,会因为流量过大,导致流量暴增,应用挂掉。所以我们引入消息中间件,可以控制活动人数。比如说在这个秒杀活动中规定只有10个产品可供抢购。

传统做法:用户点击抢购按钮发送请求后,然后处理请求,看该用户是否能抢到。使用消息中间件后,用户发送请求后,将请求信息写入消息队列,由于最终只能有10个用户可以抢到这个产品,而此时假如有10万个用户同时发送请求,我们把前10个用户的信息写入到消息队列中,10名之后的用户我们可以直接返回响应信息,直接告诉用户抢购失败,然后我们在提供专门的业务处理系统从消息队列中读取消息进行购买处理。这样的话,我们在处理购买业务时就可以只处理10个请求,而不需要处理10万个请求。

3.JMS(Java Message Service)消息模型

其实是一套规范,它规定了消息应该遵循什么样的规范。

3.1P2P(Point to Point)点对点模式

包含三个角色:消息队列(Queue)、发送者(Sender)、接收者(Receiver)。

每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到消息被消费或超时。

P2P的特点:

1)每个消息只有一个消费者,即一旦被消费,消息就不在消息队列中。即一个消息只能被一个消费者消费。

2)发送者和接收者之间在时间上没有依赖性,也就是说发送者发送了消息之后,不管接收者是否在运行,都不影响消息被发送到队列中。类比发短信时不一定要求接收者实时在线接收。

3)接收者在成功接收消息后需要给队列一个应答表示消息已成功接收到。

如果希望发送的每个消息都会被成功处理的话,那么需要P2P模式。

3.2Publish/Subscrib(Pub/Sub)发布订阅模式

包含三个角色:主题(Topic)、发布者(Publisher)、订阅者(Subscriber)

多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者,即多个订阅者可以读取同一个Topic。

Pub/Sub特点:

1)每个消息可以有多个消费者

2)发布者和订阅者之间有时间上的依赖性。针对某个主题的订阅者,它必须创建一个订阅只有,才能消费发布者的消息。

类似于订阅微信公众号之后才能接受这个主题发送的消息,但订阅之前的消息是不能接收到的。

3)为了消费信息,订阅者必须保持运行状态。

为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。

如果希望发送的消息可以被多个消费者处理的话,那么可以采用Pub/Sub模型。

4.ActiveMQ Topic消息失败重发

即当消息接收方没有成功消费消息,需要重发消息使消息的消费方成功消费消息,从而保证事务的一致性及数据一致性。

JMS消息确认机制:在Session接口中定义的几个常量:

AUTO_ACKNOWLEDGE = 1     自动确认(当消息接收方接收到消息后,自动给消息中间件一个确认信息)

CLENT_ACKNOWLEDGE = 2     客户端手动确认(客户端接收到消息且成功消费后,需要手动调用一个方法给消息中间件一个确认信息)

DUPS_OK_ACKNOWLEDGE = 3     自动批量确认

SESSION_TRANSACTED = 0      事务提交并确认

代码实现:

消息消费端在创建Session对象时需要指定应答模式为客户端手动应答,当消费者获取到消息并且成功处理后需要调用message,acknowledge()方法进行应答,通知Broker消费成功。如果处理过程中出现异常,说明消息没有成功消费,需要调用session.recover()通知Broker重消息,默认最多重复6次。

实现代码之前,先启动ActiveMQ的服务,并且保证控制台访问成功。

创建maven项目,导入依赖:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>wyh</groupId>
  <artifactId>activeMQ</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <dependencies>
  <!-- activeMQ的依赖 -->
  	<dependency>
  		<groupId>org.apache.activemq</groupId>
  		<artifactId>activemq-client</artifactId>
  		<version>5.9.0</version>
  	</dependency>
  	<dependency>
  		<groupId>junit</groupId>
  		<artifactId>junit</artifactId>
  		<version>4.12</version>
  	</dependency>
  </dependencies>
  <build>
  	<!-- 将jre的1.5改为1.8 -->
  	<plugins>
  		<!-- Java编译插件 -->
  		<plugin>
  			<groupId>org.apache.maven.plugins</groupId>
  			<artifactId>maven-compiler-plugin</artifactId>
  			<version>3.7.0</version>
  			<configuration>
  				<source>1.8</source>
  				<target>1.8</target>
  				<encoding>UTF-8</encoding>
  			</configuration>
  		</plugin>
  	</plugins>
  </build>
</project>

创建一个测试:

package wyh.activemq;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;

public class ActiveMQTest {

	// 发送消息方
	@Test
	public void test1() throws Exception {
		// 创建连接工厂对象
		ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");// 传入IP,注意是tcp协议,这里我使用的是本地的,默认端口号是61616
		// 从工厂中获取一个连接对象
		Connection connection = factory.createConnection();
		// 连接MQ服务
		connection.start();
		// 获得session对象
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 参数一:是否需要事务(一般不需要事务),参数二:消息的确认机制,常量,对于发送者来说我们设置为自动确认
		// 通过session对象创建Topic
		Topic topic = session.createTopic("MyTopic");// 参数为Topic名称
		// 通过session对象创建消息发送者
		MessageProducer producer = session.createProducer(topic);// 参数为Topic
		// 通过session创建消息对象
		TextMessage message = session.createTextMessage("wyh");// 参数为消息内容
		// 通过producer向Topic发送消息
		producer.send(message);
		// 关闭资源,倒序关闭
		producer.close();
		session.close();
		connection.close();
	}

	// 接收消息方
	@Test
	public void test2() throws Exception {
		// 创建连接工厂对象
		ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");// 传入IP,注意是tcp协议,这里我使用的是本地的,默认端口号是61616
		// 从工厂中获取一个连接对象
		Connection connection = factory.createConnection();
		// 连接MQ服务
		connection.start();
		// 获得session对象
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//参数二:消息的确认机制,常量,对于接收者我们设置为手动确认
		// 通过session对象创建Topic
		Topic topic = session.createTopic("MyTopic");// 参数为Topic名称
		// 通过session对象创建消费者
		MessageConsumer consumer = session.createConsumer(topic);//参数为Topic
		//指定消息监听器(参数为匿名内部类,MessageListener是一个接口)
		consumer.setMessageListener(new MessageListener() {
			
			//当监听的Topic里面有消息时,自动执行该方法
			@Override
			public void onMessage(Message m) {
				//强转消息,因为发送者发送的是一个textMessage
				TextMessage textMessage = (TextMessage) m;
				try {
					System.out.println("消费者接收到了消息"+textMessage.getText());
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		});
		
		// 由于接收方要一直监听MQ,需要一直在线,所以此处不关闭资源
		//由于Junit单元测试结束后,这个线程就over了,但实际应用(javaweb)中这个程序是一直执行的,所以此处我们设置一个死循环防止线程停止
		while(true) {
			
		}
		
	}

}

为了验证消息失败重发是否有效,我们先将接收方的确认机制设置为自动确认。

先启动消费者(test2),让其订阅Topic:

消费者启动成功,暂时还未接收到消息。

然后我们启动消息的发送者(test1):

再次执行消息发送者(test1)(刚才的test1没有停掉):

然后我们停掉消费者,模拟消息发送失败,此处我们制造一个算术异常,且将发送的message改为不是wyh的消息,这个时候我们再运行时test2就会走else分支,然后发生异常。(我们先使用自动确认(消费者)

package wyh.activemq;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;

public class ActiveMQTest {

	// 发送消息方
	@Test
	public void test1() throws Exception {
		// 创建连接工厂对象
		ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");// 传入IP,注意是tcp协议,这里我使用的是本地的,默认端口号是61616
		// 从工厂中获取一个连接对象
		Connection connection = factory.createConnection();
		// 连接MQ服务
		connection.start();
		// 获得session对象
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 参数一:是否需要事务(一般不需要事务),参数二:消息的确认机制,常量,对于发送者来说我们设置为自动确认
		// 通过session对象创建Topic
		Topic topic = session.createTopic("MyTopic");// 参数为Topic名称
		// 通过session对象创建消息发送者
		MessageProducer producer = session.createProducer(topic);// 参数为Topic
		// 通过session创建消息对象
		TextMessage message = session.createTextMessage("wyhsss");// 参数为消息内容
		// 通过producer向Topic发送消息
		producer.send(message);
		// 关闭资源,倒序关闭
		producer.close();
		session.close();
		connection.close();
	}

	// 接收消息方
	@Test
	public void test2() throws Exception {
		// 创建连接工厂对象
		ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");// 传入IP,注意是tcp协议,这里我使用的是本地的,默认端口号是61616
		// 从工厂中获取一个连接对象
		Connection connection = factory.createConnection();
		// 连接MQ服务
		connection.start();
		// 获得session对象
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//参数二:消息的确认机制,常量,对于接收者我们设置为手动确认
		// 通过session对象创建Topic
		Topic topic = session.createTopic("MyTopic");// 参数为Topic名称
		// 通过session对象创建消费者
		MessageConsumer consumer = session.createConsumer(topic);//参数为Topic
		//指定消息监听器(参数为匿名内部类,MessageListener是一个接口)
		consumer.setMessageListener(new MessageListener() {
			
			//当监听的Topic里面有消息时,自动执行该方法
			@Override
			public void onMessage(Message m) {
				//强转消息,因为发送者发送的是一个textMessage
				TextMessage textMessage = (TextMessage) m;
				try {
					if(textMessage.getText().equals("wyh")) {//模拟消息发送成功
						System.out.println("消费者接收到了消息"+textMessage.getText());
					}else {
						//模拟消息处理失败
						int i = 1/0;
					}
					
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		});
		
		// 由于接收方要一直监听MQ,需要一直在线,所以此处不关闭资源
		//由于Junit单元测试结束后,这个线程就over了,但实际应用(javaweb)中这个程序是一直执行的,所以此处我们设置一个死循环防止线程停止
		while(true) {
			
		}
		
	}

}

先启动test2:

再启动test1,此时并没有抛出异常(算术异常),断点可以看出它其实执行了异常,只是此处我们没有让其打印异常:

然后停掉程序,我们这次将消费者的确认机制改为手动确认,即改为CLENT_ACKNOWLEDGE,此时匿名内部类中的m需要调用acknowledge()手动确认。当消息接收失败时,调用session的recover()来告诉MQ重发消息,最多重发6次。

package wyh.activemq;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;

public class ActiveMQTest {

	// 发送消息方
	@Test
	public void test1() throws Exception {
		// 创建连接工厂对象
		ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");// 传入IP,注意是tcp协议,这里我使用的是本地的,默认端口号是61616
		// 从工厂中获取一个连接对象
		Connection connection = factory.createConnection();
		// 连接MQ服务
		connection.start();
		// 获得session对象
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 参数一:是否需要事务(一般不需要事务),参数二:消息的确认机制,常量,对于发送者来说我们设置为自动确认
		// 通过session对象创建Topic
		Topic topic = session.createTopic("MyTopic");// 参数为Topic名称
		// 通过session对象创建消息发送者
		MessageProducer producer = session.createProducer(topic);// 参数为Topic
		// 通过session创建消息对象
		TextMessage message = session.createTextMessage("wyhsss");// 参数为消息内容
		// 通过producer向Topic发送消息
		producer.send(message);
		// 关闭资源,倒序关闭
		producer.close();
		session.close();
		connection.close();
	}

	// 接收消息方
	@Test
	public void test2() throws Exception {
		// 创建连接工厂对象
		ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");// 传入IP,注意是tcp协议,这里我使用的是本地的,默认端口号是61616
		// 从工厂中获取一个连接对象
		Connection connection = factory.createConnection();
		// 连接MQ服务
		connection.start();
		// 获得session对象
		Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);//参数二:消息的确认机制,常量,对于接收者我们设置为手动确认
		// 通过session对象创建Topic
		Topic topic = session.createTopic("MyTopic");// 参数为Topic名称
		// 通过session对象创建消费者
		MessageConsumer consumer = session.createConsumer(topic);//参数为Topic
		//指定消息监听器(参数为匿名内部类,MessageListener是一个接口)
		consumer.setMessageListener(new MessageListener() {
			
			//当监听的Topic里面有消息时,自动执行该方法
			@Override
			public void onMessage(Message m) {
				//强转消息,因为发送者发送的是一个textMessage
				TextMessage textMessage = (TextMessage) m;
				try {
					if(textMessage.getText().equals("wyh")) {//模拟消息发送成功
						System.out.println("消费者接收到了消息"+textMessage.getText());
						m.acknowledge();//客户端手动确认
					}else {
						System.out.println("消息处理失败");
						//消息处理失败时,需要调用session的recover(),通知MQ进行消息的重发
						session.recover();
						//模拟消息处理失败
						int i = 1/0;
					}
					
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		});
		
		// 由于接收方要一直监听MQ,需要一直在线,所以此处不关闭资源
		//由于Junit单元测试结束后,这个线程就over了,但实际应用(javaweb)中这个程序是一直执行的,所以此处我们设置一个死循环防止线程停止
		while(true) {
			
		}
		
	}

}

启动消费者:

启动发送者(第一次发送+重发6次):

断点发现在执行完第一次输出时,执行recover方法并没有输出,而是继续执行int i = 1/0,在执行此行时会去进入到异常中,但是并没有让异常输出,执行完异常后,再执行recover方法,此时执行recover方法时会执行第一次重发,输出"消息处理失败“,然后再执行下一行的异常,就这样直到六次重发完成。

将int i = 1/0;注释掉的时候,运行结果与之前一样,

但如果将此行移动到System.out.println("消息处理失败");之前,运行结果为:

即recover方法在异常之后时,异常后面的代码不再执行。

recover方法在异常执行之前时,后面出现了异常,异常处理之后会再执行recover,直到重发6次。(这一点其实我还没太搞明白)

5.ActiveMQ Topic消息持久化订阅

5.1持久化到文件(默认)

消费者确认方式改为自动确认,取消异常情况。此时我们先不进行持久化订阅,必须先启动消费端,订阅Topic。

然后启动发送方。

如果我们先启动发送方,再启动消费方,此时消费方并没有成功消费消息。

使用持久化订阅:

第一步:在下面的文件中配置持久化适配器

找到activemq.xml,在该文件中找到如下标签:

默认把相关信息持久化到kahadb文件下:

第二步:修改消息发送方

 producer.send(message,DeliveryMode.PERSISTENT,1,1000*60*60*24);//参数一:message,参数二:使用常量告诉他持久化,参数三:优先级,参数四:持久化的时间,单位是毫秒。

第三步:修改消息接收方

//为客户端设置id,为了让MQ来区分不同的客户端
 connection.setClientID("client-01");

//客户端持久化订阅
 TopicSubscriber consumer = session.createDurableSubscriber(topic, "client-sub");//Durable持久化,Subscriber订阅

此时持久化订阅的代码如下:

package wyh.activemq;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicSubscriber;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.Test;

/**
 * Topic消息持久化订阅
 * @author Administrator
 *
 */
public class TopicPersistentTest {

	// 发送消息方
		@Test
		public void test1() throws Exception {
			// 创建连接工厂对象
			ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");// 传入IP,注意是tcp协议,这里我使用的是本地的,默认端口号是61616
			// 从工厂中获取一个连接对象
			Connection connection = factory.createConnection();
			// 连接MQ服务
			connection.start();
			// 获得session对象
			Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 参数一:是否需要事务(一般不需要事务),参数二:消息的确认机制,常量,对于发送者来说我们设置为自动确认
			// 通过session对象创建Topic
			Topic topic = session.createTopic("MyTopic");// 参数为Topic名称
			// 通过session对象创建消息发送者
			MessageProducer producer = session.createProducer(topic);// 参数为Topic
			// 通过session创建消息对象
			TextMessage message = session.createTextMessage("wyhsss");// 参数为消息内容
			// 通过producer向Topic发送消息
			producer.send(message,DeliveryMode.PERSISTENT,1,1000*60*60*24);//参数一:message,参数二:使用常量告诉他持久化,参数三:优先级,参数四:持久化的时间,单位是毫秒
			// 关闭资源,倒序关闭
			producer.close();
			session.close();
			connection.close();
		}

		// 接收消息方
		@Test
		public void test2() throws Exception {
			// 创建连接工厂对象
			ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");// 传入IP,注意是tcp协议,这里我使用的是本地的,默认端口号是61616
			// 从工厂中获取一个连接对象
			Connection connection = factory.createConnection();
			//为客户端设置id,为了让MQ来区分不同的客户端
			connection.setClientID("client-01");
			// 连接MQ服务
			connection.start();
			// 获得session对象
			Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//参数二:消息的确认机制,常量,对于接收者我们设置为手动确认
			// 通过session对象创建Topic
			Topic topic = session.createTopic("MyTopic");// 参数为Topic名称
			// 通过session对象创建消费者
			//MessageConsumer consumer = session.createConsumer(topic);//参数为Topic
			//客户端持久化订阅
			TopicSubscriber consumer = session.createDurableSubscriber(topic, "client-sub");//Durable持久化,Subscriber(订阅
			//指定消息监听器(参数为匿名内部类,MessageListener是一个接口)
			consumer.setMessageListener(new MessageListener() {
				
				//当监听的Topic里面有消息时,自动执行该方法
				@Override
				public void onMessage(Message m) {
					//强转消息,因为发送者发送的是一个textMessage
					TextMessage textMessage = (TextMessage) m;
					try {
						System.out.println("消费者接收到了消息"+textMessage.getText());
					} catch (JMSException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			});
			
			// 由于接收方要一直监听MQ,需要一直在线,所以此处不关闭资源
			//由于Junit单元测试结束后,这个线程就over了,但实际应用(javaweb)中这个程序是一直执行的,所以此处我们设置一个死循环防止线程停止
			while(true) {
				
			}
			
		}
}

先启动消费者,让其订阅Topic。


然后停掉消费者。

然后发送消息(启动发送方),再启动消费者,如果能成功消费消息,就说明我们持久化订阅成功。

5.2持久化到数据库(如Mysql)

第一步:将MySQL的数据库驱动复制到activeMQ的lib目录下。

第二步:在activemq.xml文件中配置持久化适配器(把默认的修改为下面的)。

<persistenceAdapter>
            <jdbcPersistenceAdapter dataDirectory="${activemq.base}/data" dataSource="#derby-ds"/>
  </persistenceAdapter>

第三步:在activemq.xml文件中配置数据源来连接本地的MySQL数据库(在beans标签下添加)。

<bean id="derby-ds" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://xxxxxxx:3306/activemq?relaxAutoCommit=true"/>(activemq为数据库名,需要建一个数据库
    <property name="username" value="root"/>
    <property name="password" value="root"/>
    <property name="poolPreparedStatements" value="true"/>    

</bean>

配置文件的全部代码:

<!--
    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 log searching in hawtio console -->
    <bean id="logQuery" class="org.fusesource.insight.log.log4j.Log4jLogQuery"
          lazy-init="false" scope="singleton"
          init-method="start" destroy-method="stop">
    </bean>
	
	<!-- MySql DataSource Sample Setup -->
  <bean id="mysql-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://xxxxx:3306/activemq?relaxAutoCommit=true"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
    <property name="poolPreparedStatements" value="true"/>
  </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>
		-->
		 
         <persistenceAdapter>
            <jdbcPersistenceAdapter dataDirectory="${activemq.data}/activemq-data" dataSource="#mysql-ds"/>
		</persistenceAdapter>
   
		<transportConnectors>
			<transportConnector name="default" uri="tcp://localhost:61616"/>
		</transportConnectors>
 
          <!--
            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="100 gb"/>
                </storeUsage>
                <tempUsage>
                    <tempUsage limit="50 gb"/>
                </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://127.0.0.1:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="amqp" uri="amqp://127.0.0.1:5672?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="stomp" uri="stomp://127.0.0.1:61613?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="mqtt" uri="mqtt://127.0.0.1:1883?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
            <transportConnector name="ws" uri="ws://127.0.0.1:61614?maximumConnections=1000&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>
 
    </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 -->

代码没有改变。

测试时,由于修改了配置文件,需要将activemq服务停掉,重启。

然后先启动消费方订阅Topic,然后停掉。再启动发送方,再启动消费方,此时如果接收到消息,说明使用数据库持久化订阅成功。

此时数据库中会生成三张表,记录相关信息:

以上就是初步学习activeMQ的总结,如有理解错误,恳请指出。

猜你喜欢

转载自blog.csdn.net/QYHuiiQ/article/details/84202419
今日推荐