activemq笔记

  入门

    activemq入门

    activemq 是对jms的规范实现,jms 模型图


     支持的两种基本模型 Point-to-Point(queue),Publish/Subscribe(topic),以及衍生的几种组合模型



      jms还有个broker(代理)的概念,一个单纯queue或者topic只对消息进行处理,但是broker在对消息进行处理的同时,还能对消息事故进行监控,记录,还可以自定义对消息的一些处理行为,broker提供一个独立完善的消息编程环境.一个activemq的服务就想当于一个broker(activemq,xml配置文件中有broker节点)

       相关名词介绍

  • Provider/MessageProvider:生产者
  • Consumer/MessageConsumer:消费者
  • PTP:Point To Point,点对点通信消息模型
  • Pub/Sub:Publish/Subscribe,发布订阅消息模型
  • Queue:队列,目标类型之一,和PTP结合
  • Topic:主题,目标类型之一,和Pub/Sub结合
  • ConnectionFactory:连接工厂,JMS用它创建连接
  • Connnection:JMS Client到JMS Provider的连接
  • Destination:消息目的地,由Session创建
  • Session:会话,由Connection创建,实质上就是发送、接受消息的一个线程,因此生产者、消费者都是Session创建
message :消息  

  • SreamMessage -java 原始值的数据流
  • MapMessage -Map 键值对
  • ObjectMessage -序列化的java对象
  • BytesMessage -字节流
启动rabbitmq
        启动rabbitmq,以windows为例, 下载windows压缩包 ,解压进入bin\win64目录下执行activemq.bat,即可启动,访问http://localhost:8161/admin/ ,登录名与密码均为admin. win64下有个InstallService.bat表示注册服务,开机启动

          activemq内嵌了一个broker,启动方式很简单,(具体一些其他可以自行配置),开发一般不使用内嵌

BrokerService broker = new BrokerService();
 
// configure the broker
broker.addConnector("tcp://localhost:61616");
 
broker.start();

编程代码

       本次选用采用springboot 开发环境
<?xml version="1.0" encoding="UTF-8"?>
<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>zl</groupId>
	<artifactId>jms</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>jms</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.9.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<!--spring boot集成activemq-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-activemq</artifactId>
		</dependency>

		<!--activemq 所需要jar-->
		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-all</artifactId>
			 <version>5.15.2</version>
		</dependency>

		<!--使用内置的broker -->
		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-broker</artifactId>
		</dependency>
	
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
		</dependency>

		<!--test -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.3</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

queue

queue 即消费者,生产者均为queue

p:

package jms.producer;

import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQQueue;

public class ProducerQueueDurable {
	public static void main(String[] args) {
		ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("admin","admin","tcp://localhost:61616");
		Connection connection = null ;
		try {
			connection = factory.createConnection();
			connection.start();
			//ActiveMQSession
			Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			//Topic topic = session.createTopic("hello");
			//ActiveMQQueue
			ActiveMQQueue queue = (ActiveMQQueue) session.createQueue("queue_durable");
			//ActiveMQMessageProducer
			MessageProducer producer = session.createProducer(queue);
			System.out.println(producer.getPriority());//4 0-4是普通消息,5-9是加急消息
			System.out.println(producer.getTimeToLive());//0-不过期
			System.out.println(producer.getDeliveryMode());//2
			System.out.println(producer.getDisableMessageID());//false
			System.out.println(producer.getDisableMessageTimestamp());//false
			
			for (int i = 0; i < 50; i++) {
				TextMessage message = session.createTextMessage("message "+i+" :hello world");
				producer.send(message, DeliveryMode.PERSISTENT, 4, 0);
			}
		//	session.commit();//对开启事物才有效
		} catch (JMSException e) {
			e.printStackTrace();
		} finally {
			if (connection != null) {
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

c:

package jms.consumer;

import java.io.File;
import java.util.Arrays;

import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ActiveMQSession;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.commons.io.FileUtils;

public class ConsumerQueueDurable {
	public static void main(String[] args) {

		ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("admin","admin","tcp://localhost:61616");
		Connection connection = null ;
		try {
			connection = factory.createConnection();
			//connection.setClientID("first_client_id");
			connection.start();
			//ActiveMQSession
			ActiveMQSession session = (ActiveMQSession) connection.createSession(false, ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE);
			
			//ActiveMQQueue
			ActiveMQQueue queue = (ActiveMQQueue) session.createQueue("queue_durable");
			
			
			//ActiveMQMessageConsumer
			//(MessageListener(监听),MessageSelector)
			MessageConsumer consumer = session.createConsumer(queue);
			
			
			File file = new File("C:\\Users\\Administrator\\Desktop\\temp\\t.txt");
			consumer.setMessageListener((message)->{
				if (message instanceof TextMessage) {
					TextMessage mes = (TextMessage)message;
					try {
						String text = mes.getText();
						
						System.out.println("consumer收到的消息:" + mes.getText());
						FileUtils.writeLines(file, Arrays.asList(text),true);
						mes.acknowledge();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});
			
/*			while(true) {
				TextMessage  message = (TextMessage) consumer.receive();
				//consumer.receive(timeout),consumer.receiveNoWait()
				if (message != null) {
					message.acknowledge();
					System.out.println("收到的消息:" + message.getText());
				}
			}*/
		} catch (JMSException e) {
			e.printStackTrace();
		} /*finally {
			if (connection != null) {
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}*/
	
	}

}

Topic

topic即消费者与生产者均为topic

p:

package jms.producer;

import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;

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

public class ProducerTopic {
	public static void main(String[] args) {
		ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("admin","admin","tcp://localhost:61616");
		Connection connection = null ;
		try {
			connection = factory.createConnection();
			connection.start();
			//ActiveMQSession
			Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			//Topic topic = session.createTopic("hello");
			//ActiveMQQueue
			ActiveMQTopic queue = (ActiveMQTopic) session.createTopic("queue_topic");
			//ActiveMQMessageProducer
			MessageProducer producer = session.createProducer(queue);
			
			for (int i = 0; i < 100; i++) {
				TextMessage message = session.createTextMessage("message "+i+" :hello world");
				producer.send(message, DeliveryMode.PERSISTENT, 4, 0);
			}
		//	session.commit();//对开启事物才有效
		} catch (JMSException e) {
			e.printStackTrace();
		} finally {
			if (connection != null) {
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
c:
package jms.consumer;

import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.TextMessage;

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

public class ConsumerTopic {
	public static void main(String[] args) {

		ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("admin","admin","tcp://localhost:61616");
		Connection connection = null ;
		try {
			connection = factory.createConnection();
			connection.setClientID("first_client_id");
			connection.start();
			//ActiveMQSession
			ActiveMQSession session = (ActiveMQSession) connection.createSession(false, ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE);
			
			//ActiveMQQueue
			ActiveMQTopic topic = (ActiveMQTopic) session.createTopic("queue_topic");
			
			
			//ActiveMQMessageConsumer
			//(MessageListener(监听),MessageSelector)
			MessageConsumer consumer = session.createDurableSubscriber(topic, "comsumer1");
			MessageConsumer consumer1 = session.createDurableSubscriber(topic,"comsumer2");
			
			consumer.setMessageListener((message)->{
				if (message instanceof TextMessage) {
					TextMessage mes = (TextMessage)message;
					try {
						mes.acknowledge();
						System.out.println("consumer收到的消息:" + mes.getText());
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});
			consumer1.setMessageListener((message)->{
				if (message instanceof TextMessage) {
					TextMessage mes = (TextMessage)message;
					
					try {
						mes.acknowledge();
						System.out.println("consumer1收到的消息:" + mes.getText());
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});
			
		} catch (JMSException e) {
			e.printStackTrace();
		} 
	}

}

虚拟topic

虚拟topic,即生产者为topic,消费者为queue

p:

package jms.producer;

import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;

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

public class ProducerVirtual {
	
	public static void main(String[] args) {
		ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("admin", "admin", "tcp://localhost:61616");
		Connection connection = null;
		try {
			connection = factory.createConnection();
			connection.start();
			// ActiveMQSession
			Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			
			// Topic topic = session.createTopic("hello");
			ActiveMQTopic topic = (ActiveMQTopic) session.createTopic("VirtualTopic.t");
			
			// ActiveMQMessageProducer
			MessageProducer producer = session.createProducer(topic);
			System.out.println(producer.getPriority());// 4 0-4是普通消息,5-9是加急消息
			System.out.println(producer.getTimeToLive());// 0-不过期
			System.out.println(producer.getDeliveryMode());// 2
			System.out.println(producer.getDisableMessageID());// false
			System.out.println(producer.getDisableMessageTimestamp());// false

			for (int i = 0; i < 10; i++) {
				TextMessage message = session.createTextMessage("message " + i + " :hello world");
				producer.send(message, DeliveryMode.PERSISTENT, 4, 0);
			}
			// session.commit();//对开启事物才有效
		} catch (JMSException e) {
			e.printStackTrace();
		} finally {
			if (connection != null) {
				try {
					connection.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
c:

package jms.consumer;

import java.io.File;
import java.util.Arrays;

import javax.jms.JMSException;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ActiveMQSession;
import org.apache.activemq.RedeliveryPolicy;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.commons.io.FileUtils;

public class ConsumerVirtualTopic {
	public static void main(String[] args) throws Exception {

		ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("admin", "admin", "tcp://localhost:61616");
		ActiveMQConnection connection = null;
		try {
			connection = (ActiveMQConnection) factory.createConnection();
			// connection.setClientID("first_client_id");
			RedeliveryPolicy policy = connection.getRedeliveryPolicy();
			/** destination = null,
			 *  collisionAvoidanceFactor = 0.15,
			 *  maximumRedeliveries = 6, 
			 *  maximumRedeliveryDelay = -1, 
			 *  initialRedeliveryDelay = 1000, 
			 *  useCollisionAvoidance = false, 
			 *  useExponentialBackOff = false, 
			 *  backOffMultiplier = 5.0, 
			 *  redeliveryDelay = 1000
			 * */
			policy.setMaximumRedeliveries(0);//异常不进行重试
			connection.start();
			// ActiveMQSession
			ActiveMQSession session = (ActiveMQSession) connection.createSession(false,
					ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE);

			// ActiveMQQueue
			ActiveMQQueue queueA = (ActiveMQQueue) session.createQueue("Consumer.A.VirtualTopic.t?consumer.prefetchSize=10");
			
			
			ActiveMQQueue queueB = (ActiveMQQueue) session.createQueue("Consumer.B.VirtualTopic.t?consumer.prefetchSize=10");
			
			// ActiveMQMessageConsumer
			// (MessageListener(监听),MessageSelector)
			File fileA = new File("C:\\Users\\Administrator\\Desktop\\temp\\A.txt");
			File fileB = new File("C:\\Users\\Administrator\\Desktop\\temp\\B.txt");
			session.createConsumer(queueA, (message) -> {
				if (message instanceof TextMessage) {
					TextMessage mes = (TextMessage) message;
					try {
						String text = mes.getText();
						System.out.println("consumerA收到的消息:" + mes.getText());
						// TimeUnit.SECONDS.sleep(1);
						FileUtils.writeLines(fileA, Arrays.asList(text), true);
						System.out.println("excute  " + Integer.parseInt(text.charAt(8)+""));
						mes.acknowledge();
			/*			if (Integer.parseInt(text.charAt(8)+"")%2==0) {
						} else {
							session.recover();
						}*/
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});
			session.createConsumer(queueB, (message) -> {
				if (message instanceof TextMessage) {
					TextMessage mes = (TextMessage) message;
					try {
						String text = mes.getText();
						System.out.println("consumerB收到的消息:" + mes.getText());
						FileUtils.writeLines(fileB, Arrays.asList(text), true);
						mes.acknowledge();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});
		} catch (JMSException e) {
			e.printStackTrace();
		} 
	}
}
自定义虚拟topic,activemq.xml的broker中设置

<destinationInterceptors> 
 <virtualDestinationInterceptor> 
 <virtualDestinations> 
 <virtualTopic name=">" prefix="VirtualTopicConsumers.*." selectorAware="false"/>    
   </virtualDestinations>
 </virtualDestinationInterceptor> 
</destinationInterceptors>
默认的虚拟topic

name="VirtualTopic.>",
prefix="Consumer.*."
其他属性

activemq 重发

重发触发条件

  • 开启事物,进行rollback()
  • 开启事物,session关闭,但是还没有commit
  • session 使用CLIENT_ACKNOWLEDGE 并且进行Session.recover() 
  • 超时

重发主要通过RedeliveryPolicy控制,其相关属性如下


useExponentialBackOff:控制backOffMultiplier(重发间隔递增的倍数)
useCollisionAvoidance:控制collisionAvoidanceFactor(设置重发间隔时间波动范围)
initialRedeliveryDelay:初始重发间隔时间
redeliveryDelay:重发间隔时间(initialRedeliveryDelay=0才会生效)
maximumRedeliveries:重发次数(-1,不限制次数)
maximumRedeliveryDelay:重发最大的时间间隔(-1,不限制)

代码控制
ActiveMQConnection connection ... // Create a connection
 
RedeliveryPolicy queuePolicy =newRedeliveryPolicy();
queuePolicy.setInitialRedeliveryDelay(0);
queuePolicy.setRedeliveryDelay(1000);
queuePolicy.setUseExponentialBackOff(false);
queuePolicy.setMaximumRedeliveries(2);
 
RedeliveryPolicy topicPolicy =newRedeliveryPolicy();
topicPolicy.setInitialRedeliveryDelay(0);
topicPolicy.setRedeliveryDelay(1000);
topicPolicy.setUseExponentialBackOff(false);
topicPolicy.setMaximumRedeliveries(3);
 
// Receive a message with the JMS API
RedeliveryPolicyMap map = connection.getRedeliveryPolicyMap();
map.put(newActiveMQTopic(">"), topicPolicy);
map.put(newActiveMQQueue(">"), queuePolicy);
重发失败后将进入死信队列(默认Activemq.DLQ)
自定义DLQ
<broker>
   
  <destinationPolicy>
    <policyMap>
      <policyEntries>
        <!-- Set the following policy on all queues using the'>'wildcard -->
        <policyEntry queue=">">
          <deadLetterStrategy>
            <!--
              Use the prefix'DLQ.'forthe destination name, and make
              the DLQ a queue rather than a topic
            -->
            <individualDeadLetterStrategy queuePrefix="DLQ."useQueueForQueueMessages="true"/>
          </deadLetterStrategy>
        </policyEntry>
      </policyEntries>
    </policyMap>
  </destinationPolicy>
   
</broker>

通配符

一般情况下,我们使用层次结构的方式来组织队列,比如A.B.C.D,这样便于归类和管理。
我们也可以使用通配符来配置或是操作多个队列。
通配符有三个:
.  用来分隔路径
* 用来匹配路径中的一节
> 用来匹配任意节的路径
示例:
PRICE.> 以PRICE.开头

自定义分隔符(5.5版本后)
<plugins> ..... <destinationPathSeparatorPlugin/> </plugins>
定义以上FOO.BAR.* 可由 FOO/BAR/*表示

prefetch

   prefetch指消息的预获取,指消费者可获取的最大消息量,通过ActiveMQPrefetchPolicy策略控制

默认
  • persistent queues (default value: 1000)
  • non-persistent queues (default value: 1000)
  • persistent topics (default value: 100)
  • non-persistent topics (default value: Short.MAX_VALUE - 1)
java代码
private int queuePrefetch=1000;
private int queueBrowserPrefetch=500;
private int topicPrefetch=32767;
private int durableTopicPrefetch=100;
private int optimizeDurableTopicPrefetch=1000;
private int maximumPendingMessageLimit;
可通过 ActiveMQConnectionFactory ActiveMQConnection 设置
对所有consumer有效
tcp://localhost:61616?jms.prefetchPolicy.all=50
对queue有效
tcp://localhost:61616?jms.prefetchPolicy.queuePrefetch=1
通过Destination设置
queue =newActiveMQQueue("TEST.QUEUE?consumer.prefetchSize=10");
consumer = session.createConsumer(queue);

集成jndi

新建jndi.propeties文件
java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url = tcp://localhost:61616
queue.MyQueue = example.MyQueue
topic.MyTopic = example.MyTopic
编码
package jms.jndi;

import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class JmsJndi {

	public static void main(String[] args) {
		Context ctx = null;
		TopicConnection conn=null;
		try {
			ctx = new InitialContext();// 默认读取jndi.properties可自定义配置文件InitialContext(Hashtable<?,?>environment)

			TopicConnectionFactory factory = (javax.jms.TopicConnectionFactory) ctx.lookup("ConnectionFactory");
			conn = factory.createTopicConnection("admin", "admin");
			conn.start();
			Topic mytopic = (javax.jms.Topic) ctx.lookup("MyTopic");
			TopicSession session = conn.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);
			
			MessageConsumer consumer = session.createSubscriber(mytopic);
			consumer.setMessageListener((message)->{
				if (message instanceof TextMessage) {
					TextMessage m =	(TextMessage)message;
					try {
						System.out.println("A receive" + m.getText());
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});
			
			MessageConsumer consumer1 = session.createSubscriber(mytopic);
			consumer1.setMessageListener((message)->{
				if (message instanceof TextMessage) {
					TextMessage m =	(TextMessage)message;
					try {
						System.out.println("B receive" + m.getText());
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});
			
			MessageProducer producer = session.createProducer(mytopic);
			for (int i = 0; i < 10; i++) {
				producer.send(session.createTextMessage(String.format("the %d time hello world",i)));
				System.out.println(String.format(" send messgae : the %d time hello world",i));
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (ctx != null) {
				try {
					ctx.close();
				} catch (NamingException e) {
					e.printStackTrace();
				}
			}
/*			if (conn != null) {
				try {
					conn.close();
				} catch (JMSException e) {
					e.printStackTrace();
				}
			}*/
		}

	}

}

持久化

持久化对象: queue, topic, 持久化subscribe,持久化消息 以及彼此之间的关系.
activemq中的factory,connection,session都是随用随建,用完就关,并不需要持久化.queue,topic类型消息通道实例,大多不轻易改变,有持久化必要, subscribe(durable,offline durable,non-durable三种)根据实用场景选择是否有必要持久化 , 消息作为最重要的载体,为保证消息不丢失,可选持久化.
activemq 提供了多种 持久化, KahaDB(推荐) , AMQ Message Store( Replicated LevelDB Store), JDBC,Memory Message Store(内存,不持久化),前面两种是基于文件存储的快速(High performance journal)存储,而JDBC则处理较慢,但两者可同时使用.
5.3版本之后推荐使用KahaDb(默认的持久化),相比AMQ其性能与恢复能力更强,但AMQ更快,

kahaDb

设置

<broker brokerName="broker">
   <persistenceAdapter>
     <kahaDB directory="activemq-data" journalMaxFileLength="32mb"/>
   </persistenceAdapter>
</broker>
默认的路径${activemq.data}/kahadb,下有4个文件( 参考)
  • db.data消息的索引文件,使用B-Tree作为索引指向db-*.log里面存储的消息
  • db.redo消息恢复
  • db-*.log存储消息(message data,Destinations,订阅关系,事务...)
  • lock 锁
重要属性:
  • indexWriteBatchSize  默认值1000,当Metadata Cache中更新的索引到达了1000时,才同步到磁盘上的Metadata Store中。不是每次更新都写磁盘,而是批量更新写磁盘,比较写磁盘的代价是很大的
  • indexCacheSize      默认值10000,(number of index pages cached in memory),在内存中最多分配多个页面来缓存index。缓存的index越多,命中的概率就越大,检索的效率就越高。
  • journalMaxFileLength  默认值32MB,当存储的消息达到32MB时,新建一个新文件来保存消息。这个配置对生产者或消息者的速率有影响。比如,生产者速率很快而消费者速率很慢时,将它配置得大一点比较好。
  • enableJournalDiskSyncs  默认值true,默认采用同步写磁盘,即消息先存储到磁盘中再向Producer返回ACK
  • cleanupInterval  默认值30000ms,当消息被消息者成功消费之后,Broker就可以将消息删除了。
  • checkpointInterval  默认值5s,每隔5s将内存中的Index(Metadata Cache)更新到磁盘的Index文件中(Metadata Store)

游标

之前的在消息分发之前,都是在内存中保持其引用,当大量消息就有可能撑爆内存,5.0版本之后,引入新的内存模式,在空间可用时允许消息从存储设备中以页为单位进入(对持久化消息使用存储游标),Store based,VM Cursor,File based Cursor
5.0之后,activemq对游标的运用分两种,消费者跟的上生产的速度(不使用游标),消费者跟不上生产的速度(使用游标)--消息都会先存储
消费者速率过快(dispatching message for fast consumers)


消费者速率过慢(dispatching message for slowconsumers)

Store Based

默认的存储方式,非持久化消息直接被传递给游标,所以针对这些消息,基于存储的游标嵌入了一个基于文件的游标


VM Cursor

   基于内存优势在于快,但是在针对长时间不活跃或者处理非常慢的消费者,效率很低

File based Cursor

基于文件的游标是从虚拟内存游标衍生而来的。当代理的内存达到限度之后,它可以将消息写入磁盘上的临时文件。这种类型的游标适用的场景是,消息存储相对要慢,但是消费者要快一些。通过在磁盘上做缓冲,消息代理可以在应对消息爆发的时候,不需要从慢存储中读取。

配置

topic subscribes
对所有的主题来说,每一个订阅者都有一个分发队列和挂起的游标。可以针对持久订阅者和短暂订阅者分别配置不同的策略,例如:
<destinationPolicy>
      <policyMap>
        <policyEntries>
          <policyEntry topic="org.apache.>" producerFlowControl="false" memoryLimit="1mb">
            <dispatchPolicy>
              <strictOrderDispatchPolicy />
            </dispatchPolicy>
            <deadLetterStrategy>
              <individualDeadLetterStrategy  topicPrefix="Test.DLQ." />
            </deadLetterStrategy>
            <pendingSubscriberPolicy>
                <vmCursor />
            </pendingSubscriberPolicy>
            <pendingDurableSubscriberPolicy>
                <vmDurableCursor/>
            </pendingDurableSubscriberPolicy>
          </policyEntry>
        </policyEntries>
      </policyMap>
</destinationPolicy>
     有效的订阅者游标类型是vmCursor 和fileCursor。默认情况下是基于存储的游标。
     有效的持久订阅者类游标类型是storeDurableSubscriberCursor,vvmDurableCursor 和 fileDurableSubscriberCursor。默认情况下是基于存储的游标。
Queues
对于队列来说,每一个目的地都有一个单独的分发队列和挂起队列,所以配置略微有些不同:
<destinationPolicy>
      <policyMap>
        <policyEntries>
          <policyEntry queue="org.apache.>">
            <deadLetterStrategy>
              <individualDeadLetterStrategy queuePrefix="Test.DLQ."/>
            </deadLetterStrategy>
            <pendingQueuePolicy>
                <vmQueueCursor />
            </pendingQueuePolicy>
          </policyEntry>
        </policyEntries>
      </policyMap>
 </destinationPolicy>
    有效的队列游标类型是 vmQueueCursor 和 fileQueueCursor。默认情况下是基于存储的游标

dispatch

queue

<policyEntry /> element. E.g.:
<policyEntry queue=">" strictOrderDispatch="false" />
     默认strictOrderDispatch=false则轮流消费( round robin,每个消费者取prefetchSize量消息),为true表示按序消费,通过设置优先级设置顺序:
queue = new ActiveMQQueue("TEST.QUEUE?consumer.priority=10");
consumer = session.createConsumer(queue);

Topic

      topic分发策略则比较丰富,有内置,也可以自定义
  • SimpleDispatchPolicy.默认,轮训分发
  • PriorityNetworkDispatchPolicy 权重only dispatch to the highest priority network consumer
  • PriorityDispatchPolicy  权重匹配( Priority dispatch policy that sends a message to every subscription that matches the message in consumer priority order)
  • RoundRobinDispatchPolicy 类似SimpleDispatchPolicy
  • StrictOrderDispatchPolicy   Dispatch policy that causes every subscription to see messages in the same order.
示例
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry topic="FOO.>">
<dispatchPolicy>
<roundRobinDispatchPolicy/>
</dispatchPolicy>
<subscriptionRecoveryPolicy>
<lastImageSubscriptionRecoveryPolicy/>
</subscriptionRecoveryPolicy>
</policyEntry>
<policyEntry topic="ORDERS.>">
<dispatchPolicy>
<strictOrderDispatchPolicy/>
</dispatchPolicy>
<!--   1 minutes worth  -->
<subscriptionRecoveryPolicy>
<timedSubscriptionRecoveryPolicy recoverDuration="60000"/>
</subscriptionRecoveryPolicy>
</policyEntry>
<policyEntry topic="PRICES.>">
<!--
 lets force old messages to be discarded for slow consumers 
-->
<pendingMessageLimitStrategy>
<constantPendingMessageLimitStrategy limit="10"/>
</pendingMessageLimitStrategy>
<!--   10 seconds worth  -->
<subscriptionRecoveryPolicy>
<timedSubscriptionRecoveryPolicy recoverDuration="10000"/>
</subscriptionRecoveryPolicy>
</policyEntry>
<policyEntry tempTopic="true" advisoryForConsumed="true"/>
<policyEntry tempQueue="true" advisoryForConsumed="true"/>
</policyEntries>
</policyMap>
</destinationPolicy>
      自定义分发

Destination Policy 

      每个destination都可定义其策略,xml定义
<policyEntry queue=">" strictOrderDispatch="false" />
仅列举几处
  • useConsumerPriority 默认true,queue是否参考priority 
  • strictOrderDispatch  false  严格分发
  • optimizedDispatch false 分发queue是否额外开启线程
  • lazyDispatch  false queue是否懒分发
  • queuePrefetch  queue每次prefetch值
  • ......

SpringBoot整合activemq

Application
package jms;

import javax.jms.ConnectionFactory;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;

import jms.vo.Email;

@SpringBootApplication
@EnableJms
public class JmsApplication {

    @Bean
    public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory,
                                                    DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        // This provides all boot's default to this factory, including the message converter
        configurer.configure(factory, connectionFactory);//connectionFactory使用的内置broker,可自定义connectionFactory
        // You could still override some of Boot's default if necessary.
        return factory;
    }

    @Bean
    public MessageConverter jacksonJmsMessageConverter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        converter.setTargetType(MessageType.TEXT);
        converter.setTypeIdPropertyName("_type");
        return converter;
    }

    public static void main(String[] args) {
        // Launch the application
        ConfigurableApplicationContext context = SpringApplication.run(JmsApplication.class, args);
        JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
        // Send a message with a POJO - the template reuse the message converter
        System.out.println("Sending an email message.");
        jmsTemplate.convertAndSend("mailbox", new Email("info@example.com", "Hello"));
    }

}
Receiver
package jms.receiver;

import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;

import jms.vo.Email;

@Component
public class Receiver {

    @JmsListener(destination = "mailbox", containerFactory = "myFactory")
    public void receiveMessage(Email email) {
        System.out.println("Received <" + email + ">");
    }

}
消息模板
package jms.vo;

public class Email {

	private String to;
	private String body;

	public Email() {
	}

	public Email(String to, String body) {
		this.to = to;
		this.body = body;
	}

	public String getTo() {
		return to;
	}

	public void setTo(String to) {
		this.to = to;
	}

	public String getBody() {
		return body;
	}

	public void setBody(String body) {
		this.body = body;
	}

	@Override
	public String toString() {
		return String.format("Email{to=%s, body=%s}", getTo(), getBody());
	}

}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值