文章目录
第一部分 消息系统和Activemq
JMS
JMS概念
- JMS Client:纯JAVA 发送和接收消息 的客户端
- Non-JMS Client:使用JMS Provider的本地API 代替JMS 发送和接收消息的客户端。
- JMS Producer
- JMS Consumer
- JMS Provider :纯JAVA实现JMS接口 的应用
- JMS Message
- JMS Domains:消息系统的2种模式,包括点对点、发布/订阅
- Administered Objects
• Connection Factory
• Destination
public interface MessageProducer {
void setDisableMessageID(boolean value) throws JMSException;
boolean getDisableMessageID() throws JMSException;
void setDisableMessageTimestamp(boolean value) throws JMSException;
boolean getDisableMessageTimestamp() throws JMSException;
void setDeliveryMode(int deliveryMode) throws JMSException;
int getDeliveryMode() throws JMSException;
void setPriority(int defaultPriority) throws JMSException;
int getPriority() throws JMSException;
void setTimeToLive(long timeToLive) throws JMSException;
long getTimeToLive() throws JMSException;
Destination getDestination() throws JMSException;
void close() throws JMSException;
void send(Message message) throws JMSException;
void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException;
void send(Destination destination, Message message) throws JMSException;
void send( Destination destination, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException;
}
public interface MessageConsumer {
String getMessageSelector() throws JMSException;
MessageListener getMessageListener() throws JMSException;
void setMessageListener(MessageListener listener) throws JMSException;
Message receive() throws JMSException;
Message receive(long timeout) throws JMSException;
Message receiveNoWait() throws JMSException;
void close() throws JMSException;
}
JMS消息
一个消息有三个主要部分:
- 消息头(Header,必须):包含用于识别和为消息寻找路由的操作设置,所有类型的这部分格式都是一样的。
- 消息体(Body,可选、一个):指我们具体需要消息传输的内容,允许用户创建五种类型的消息(文本消息,映射消息,字节消息,流消息和对象消息)。
- 消息属性(Properties,可选、一组):按类型可以分为应用设置的属性,标准属性和消息中间件定义的属性,包含额外的属性,支持其他提供者和用户的兼容。可以创建定制的字段和过滤器(消息选择器)。
Message消息头(Header)
属性名称 | 说明 | 设置者 |
---|---|---|
JMSDestination | 消息发送的目的地,是一个Topic或Queue | send |
JMSDeliveryMode | 消息的发送模式,分为NON_PERSISTENT和PERSISTENT,即持久化的和非持久化的 | send |
JMSMessageID | 消息ID,需要以ID: 开头 | send |
JMSTimestamp | 消息发送时的时间,也可以理解为调用send() 方法时的时间,而不是该消息发送完成的时间 | send |
JMSCorrelationID | 关联的消息ID,这个通常用在需要回传消息的时候 | client |
JMSReplyTo | 消息回复的目的地,其值为一个Topic或Queue, 这个由发送者设置,但是接收者可以决定是否响应 | client |
JMSRedelivered | 消息是否重复发送过,如果该消息之前发送过,那么这个属性的值需要被设置为true, 客户端可以根据这个属性的值来确认这个消息是否重复发送过,以避免重复处理。 | Provider |
JMSType | 由消息发送者设置的个消息类型,代表消息的结构,有的消息中间件可能会用到这个,但这个并不是是批消息的种类,比如TextMessage之类的 | client |
JMSExpiration | 消息的过期时间,以毫秒为单位,根据定义,它应该是timeToLive的值再加上发送时的GMT时间,也就是说这个指的是过期时间,而不是有效期 | send |
JMSPriority | 消息的优先级,0-4为普通的优化级,而5-9为高优先级,通常情况下,高优化级的消息需要优先发送 | send |
系统提供的标准头信息一共有10个属性,其中有6个是由send
方法在调用时设置的,有三个是由客户端设置的,还有一个是由消息中间件设置的。
注意:这里的client
不是指消费者,而是指使用JMS的客户端,即开发者所写的应用程序,即在生产消息时,这三个属性是可以由应用程序来设定的,而其它的header要么由消息中间件设置,要么由发送方法来决定,开发者即使设置了,也是无效的。
Message消息体(Body)
注意:发送和接受的消息体类型必须保持一一对应。
五中消息体格式
序号 | 消息体类型 | 说明 |
---|---|---|
1 | TextMessage(文本消息) | 编码字符串。对于外发消息,字符串在由目标对象给定的字符集中进行编码。缺省情况下使用 UTF8 编码(UTF8 编码从消息的第一个字符开始;开头处无长度字段)。但是,可以指定 用于 JMS 的 IBM® MQ 类 支持的任何其他字符集。此类字符集主要在将消息发送到非 JMS 应用程序时使用。 如果字符集是双字节集(包括 UTF16),那么目标对象的整数编码规范可确定字节顺序。 使用消息本身中指定的字符集及编码来解释入局消息。这些规范在最后一个 IBM MQ 头中,如果没有头,那么在 MQMD 中。对于 JMS 消息,最后一个头通常为 MQRFH2。 |
2 | MapMessage(键值对消息) | 消息体包含了一系列的名字-值对.名字是Strings,而值则是Java primitive 类型.消息体中的条目可以被enumerator按照顺序访问,也可以自由访问.条目的顺序没 有定义. |
3 | ObjectMessage(对象消息) | 是 Java™ Runtime 以正常方式进行序列化的对象。 |
4 | BytesMessage(bytes消息) | 缺省情况下,BytesMessage 是 JMS 1.0.2 规范及关联 Java 文档所定义的一系列字节。 对于由应用程序本身组合的外发消息,目标对象的编码属性可用于覆盖消息中所含的整数和浮点字段的编码。例如,可以请求以 S/390 格式而非 IEEE 格式存储浮点值。 使用消息本身中指定的数字编码来解释入局消息。此规范在最后一个 IBM MQ 头中,如果没有头,那么在 MQMD 中。对于 JMS 消息,最后一个头通常为 MQRFH2。 如果收到 BytesMessage,并且在不进行修改的情况下重新发送,那么消息主体将按照其接收的方式逐字节进行传输。目标对象的编码属性对主体无任何影响。可以在 BytesMessage 中明确发送的唯一的类似字符串的实体是 UTF8 字符串。它采用 Java UTF8 格式编码,并以双字节长度字段开头。目标对象的字符集属性对外发 BytesMessage 编码无任何影响。入局 IBM MQ 消息中的字符集值对于将此消息解释为 JMS BytesMessage 无任何影响。 非 Java 应用程序不太可能能够识别 Java UTF8 编码。因此,对于要发送包含文本数据的 BytesMessage 的 JMS 应用程序,应用程序本身必须将其字符串转换为字节数组,并将这些字节数组写入 BytesMessage。 |
5 | StreamMessage(流消息) | StreamMessage 与映射消息类似,但无元素名称 |
Message消息属性
属性 | 说明 |
---|---|
JMSXUserID | 发送消息的用户识别,发送时提供商设置 |
JMSXappID | 发送消息的应用标识,发送时提供商设置 |
JMSXdeliveryCount | 转发消息重试次数:从1开始,发送方提供商设置 |
JMSXGroupID | 消息所在消息组的用户标识,由客户端设置 |
JMSXGroupSeq | 组内消息的序号,从1开始.由客户端设置 |
JMSXProducerTEID | 产生消息的事务的事务表示,发送方提供商设置 |
JMSConsumerTXID | 消费消息的事务的事务表示,接收方提供设置 |
JMSXRevTimestamp | JMS转发消息到消费者的事件,接收方提供设置 |
JMState | 假设有个消息仓库,它存储每个消息的单独拷贝,从原始消息被发送时开始,状态有1(等待),2(准备),3(到期),4(保留),由于状态和生产者和消费者无关,所以它不是由他们提供,它只和仓库查找消息相关,因此JMS没有提供这中API,由提供商设置 |
消息选择器
消息可包含应用程序定义的属性值。 应用程序可以使用消息选择器来获取 JMS 提供程序过滤器消息。
消息包含一项内置功能,来支持应用程序定义的属性值。 实际上,这提供了一种机制,来将特定于应用程序的头字段添加到消息中。 通过使用消息选择器,属性允许应用程序使用特定于应用程序的条件,让 JMS 提供程序代表其选择或过滤消息。 应用程序定义的属性必须遵循以下规则:
- 属性名称必须遵循消息选择器标识规则。
- 属性值可以是 Boolean、byte、short、int、long、float、double 和 String。
- 将保留 JMSX 和 JMS_ name 前缀。
JMS 消息选择器允许客户机通过使用消息头来指定其感兴趣的消息。 仅传送其头与选择器匹配的消息。
消息选择器无法参考消息体值。
在选择器中将消息头字段和属性值替换为其对应标识时,如果选择器求值结果为 true,那么表示消息选择器将与消息匹配。
消息选择器为字符串,其语法基于 SQL92 条件表达式语法的一个子集。 消息选择器的求值顺序为同一优先顺序级别内从左到右。 可使用括号来更改这一顺序。 预定义选择器字面值和运算符名称在此处以大写写入;但是,这些文本和运算符不区分大小写。
"JMSType = 'car' AND color = 'blue' AND weight > 2500"
-- 如果应用程序指定以下格式的选择器:
-- JMSMessageID = "标识:message_id"
JMSMessageID='ID:414D51207061756C745639314C545320C57C1A5F25ECE602'
JMSCorrelationID='ID:414D51207061756C745639314C545320846E5B5F25B1CC02'
-- 如果消息选择器包含 message_id 或 correlation_id 全都为零的值,那么将与队列上的任何消息相匹配。 例如,如果应用程序使用选择器:
JMSMessageID='ID:000000000000000000000000000000000000000000000000'
参考:https://www.ibm.com/docs/zh/ibm-mq/9.1?topic=messages-message-selectors-in-jms
第二部分 配置Activemq
第4章 Connecting to ActiveMQ
ActiveMQ提供connectors
连接机制用于客户端连接brokers,可用于broker-to-broker。客户端可以使用多种协议连接broker。
Connector URIs
ActiveMQ使用 low-level connectors
进行连接。
格式:
schema://path?query
#exsample
tcp://localhost:61614?trace=true
Configuring Transport Connectors
配置文件:conf/activemq.xml
<!-- The transport connectors ActiveMQ will listen to,name必须唯一 -->
<transportConnectors>
<transportConnector name="openwire" uri="tcp://localhost:61616"
discoveryUri="multicast://default"/>
<transportConnector name="ssl" uri="ssl://localhost:61617"/>
<transportConnector name="stomp" uri="stomp://localhost:61613"/>
<transportConnector name="xmpp" uri="xmpp://localhost:61222"/>
</transportConnectors>
可以通过以下方式创建连接
ActiveMQConnectionFactory factory =
new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = factory.createConnection();
connection.start();
Session session =
connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
生产者
Publisher publisher = new Publisher(args[0]);
String[] topics = new String[args.length - 1];
publisher.sendMessage(topics);
消费者
Consumer consumer = new Consumer(args[0]);
String[] topics = new String[args.length - 1];
for (String stock : topics) {
Destination destination = consumer.getSession().createTopic("STOCKS." + stock);
MessageConsumer messageConsumer = consumer.getSession().createConsumer(destination);
messageConsumer.setMessageListener(new Listener());
}}
Using Network Protocols
TCP
消息序列化为字节序列,通过 wire protocol
来定义,activemq默认使用的 wire protocol 是 OpenWire
。
TCP transport connector 用于转换 message 序列化为 OpenWire
格式并通过 TCP network。
#格式
tcp://hostname:port?key=value&key=value
#默认端口 61616
配置:
<transportConnectors>
<transportConnector
name="tcp"
uri="tcp://localhost:61616?trace=true"
/>
</transportConnectors>
options:
trace:记录所有的操作
优点
- 高效
- 可用性:常用协议
- 可靠性:
NIO (New I/O API Protocol)
格式:
nio://hostname:port?key=value
配置:
<transportConnectors>
<transportConnector
name="tcp"
uri="tcp://localhost:61616?trace=true" />
<transportConnector
name="nio"
uri="nio://localhost:61618?trace=true" />
</transportConnectors>
UDP
格式:
udp://hostname:port?key=value
配置:
<transportConnectors>
<transportConnector
name="tcp"
uri="tcp://localhost:61616?trace=true"
/>
<transportConnector
name="udp"
uri="udp://localhost:61618?trace=true"
/>
</transportConnectors>
SSL
格式:
ssl://hostname:port?key=value
配置:
<transportConnectors>
<transportConnector
name="tcp"
uri="tcp://localhost:61616?trace=true"
/>
<transportConnector
name="ssl"
uri="ssl://localhost:61617?trace=true"
/>
</transportConnectors>
SSL协议需要设置
certificate
。JSEE支持2种文件格式用于存储keys和certificates。第一种
keystores
,存储私有数据,凭据。受信certificate存储在truststores
。默认存储位置:
${ACTIVEMQ_HOME}/conf/
,broker.ks
是默认broker certificate的keystore。broker.ts
是默认truststores。
使用SSL
//系统属性
javax.net.ssl.keyStore //定义一个keystore
javax.net.ssl.keyStorePassword //定义keystore password
javax.net.ssl.trustStore // 定义 truststore
mvn \
-Djavax.net.ssl.keyStore=${ACTIVEMQ_HOME}/conf/client.ks \
-Djavax.net.ssl.keyStorePassword=XXX \
-Djavax.net.ssl.trustStore=${ACTIVEMQ_HOME}/conf/client.ts \
exec:java -Dexec.mainClass=org.apache.activemq.book.ch3.Publisher \
-Dexec.args="ssl://localhost:61617 CSCO ORCL"
keytool
工具可用于生成keystore和certificate
keytool -genkey -alias broker -keyalg RSA -keystore mybroker.ks
keytool -export -alias broker -keystore mybroker.ks -file mybroker_cert
keytool -genkey -alias client -keyalg RSA -keystore myclient.ks
HTTP/HTTPS
格式:
http://hostname:port?key=value
https://hostname:port?key=value
配置:
<transportConnectors>
<transportConnector
name="tcp"
uri="tcp://localhost:61616?trace=true"
/>
<transportConnector
name="http"
uri="http://localhost:8080?trace=true"
/>
</transportConnectors>
客户端使用http协议,必须加入以下jar包
$ACTIVEMQ_HOME/lib/optional/activemq-optional-<version>.jar
$ACTIVEMQ_HOME/lib/optional/commons-httpclient-<version>.jar
$ACTIVEMQ_HOME/lib/optional/xstream-<version>.jar
$ACTIVEMQ_HOME/lib/optional/xmlpull-<version>.jar
maven 依赖:
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-optional</artifactId>
<version>5.2.0</version>
</dependency>
Virtual Machine Protocol
使用场景:activemq 嵌入一个java 应用中。不通过网络访问,只通过方法调用
格式:
vm://brokerName?key=value #brokerName必须唯一,可以创建多个
#示例
vm://broker1?marshal=false&broker.persistent=false
#配置额外的connector
vm:broker:(transportURI,network:networkURI)/brokerName?key=value
#vm:broker:(tcp://localhost:6000)?brokerName=embeddedbroker&persistent=false
Configuring Network Connectors
Network Connectors 用于配置 brokers 之间的网络拓扑结构。
<!-- The store and forward broker networks ActiveMQ will listen to -->
<networkConnectors>
<!-- by default just auto discover the other brokers -->
<networkConnector name="default-nc" uri="multicast://default"/>
<!--
<networkConnector name="host1 and host2"
uri="static://(tcp://host1:61616,tcp://host2:61616)"/>
-->
</networkConnectors>
discovery: 指发现其他broker的过程。
Defining Static Networks
配置静态网络,前提是知道所有broker的uri。
static protocol
格式:
static:(uri1,uri2,uri3,...)?key=value
配置:
<networkConnectors>
<networkConnector name="local network"
uri="static://(tcp://remotehost1:61616,tcp://remotehost2:61616)"/>
</networkConnectors>
Failover Protocol
故障转移
格式:
failover:(uri1,...,uriN)?key=value
或者
failover:uri1,...,uriN
Defining Dynamic networks
Multicast Protocol
广播协议。Group address: 224.0.0.0 to 239.255.255.255,
配置:
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="multicast"
dataDirectory="${activemq.base}/data">
<networkConnectors>
<networkConnector name="default-nc" uri="multicast://default"/>
</networkConnectors>
<transportConnectors>
<transportConnector name="openwire" uri="tcp://localhost:61616" discoveryUri="multicast://default"/>
</transportConnectors>
</broker>
缺点:
- 自动发现,容易加入不应该加入的broker
- 建议谨慎使用
阻止自动broker发现:
当在一个团队环境,使用ActiveMq的默认配置,有可能导致 一个Activemq实例去消费另一个实例的消息,有一些建议用于避免此场景:
移除
Openwire transport connector
的discoveryUri
选项,此选项让其他broker发现自己。<transportConnector name="openwire" uri="tcp://localhost:61616" />
移除
default-nc network connector
,此选项用于 发现其他 broker。<!--networkConnector name="default-nc" uri="multicast://default"/-->
为broker确定唯一的名称:默认为localhost,
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="broker1234" dataDirectory="${activemq.base}/data">
Discovery Protocol
discovery transport connector
是客户端 的广播功能,行为上类似failover协议,区别是可以通过广播发现broker,并随机选择一个连接。
格式:
discovery:(discoveryAgentURI)?key=value
Peer Protocol
前述方式连接内嵌broker是比较笨重的,Activemq提供了 Peer Protocol
可以很容易连接内嵌brokers。应用内的消费者和生产者访问内嵌的broker,不同application内的brokers之间可以通过network串联。
格式:
peer://peergroup/brokerName?key=value
Fanout Protoco
此协议允许客户端同时连接多个broker,并且复制操作到这些broker。
格式:
fanout:(fanoutURI)?key=value
fanoutURI,可以是static URI
或 multicast URI
第5章 消息持久化
Activemq不仅支持JMS规范的数据传递持久化或非持久化模式,还支持数据恢复。
Activemq支持插件策略支持消息存储,以及为in-memory方式提供存储选项:文件模式或关系型数据库模式
一旦消息被消费者消费,并且确认Q,就会被删除。
消息存储
queue和topic的存储模式不完全相同,涉及到point-to-point
和publish/subscribe
的相关领域
queue采用FIFO模式直接存储,消息只会发给一个consumer,并且消费确认只会,直接删除。
topic模式,每个consumer都可以获取message的副本,一条消息只会存储在一个broker上。订阅者只能接收订阅之后的消息。
topic存储
每个消息存储实现都必须支持queue和topic 的持久化。
消息存储实现
- AMQ:为了性能和可用性的默认实现。
- KahaDB:实现可扩展性和可恢复。
- JDBC:
AMQ
消息存储的默认实现,基于文件系统的事务性存储,经过性能调校,用于快速消息存储。目标:易于使用以及尽可能快。
在 activemq.xml
中使用 持久化适配器 来配置。
...
<broker persistent="true" xmlns="http://activemq.apache.org/schema/core">
...
<persistenceAdapter>
<amqPersistenceAdapter/>
</persistenceAdapter>
...
</broker>
...
嵌入Activemq,可以采用以下方式:
public class EmbeddedBrokerUsingAMQStoreExample {
public void main(String[] args) throws Exception {
BrokerService broker = new BrokerService();
PersistenceAdapterFactory persistenceFactory = new AMQPersistenceAdapterFactory();·
persistenceFactory.setMaxFileLength(1024*16);
persistenceFactory.setPersistentIndex(true);
persistenceFactory.setCleanupInterval(10000);
broker.setPersistenceFactory(persistenceFactory);¸
broker.addConnector("tcp://localhost:61616");¹
broker.start();
}
}
AMQ存储内核
AMQ是最快的实现。基于高优化的消息id索引以及内存缓存。
AMQ的主要组件:
-
The Journal:
由消息的滚动日志、命令(事务边界和消息删除)组成,存储在确定长度的文件中。当文件长度达到指定大小,会创建一个新的文件。
数据文件中的所有消息都有引用计数,一旦数据文件的每条消息都不再被引用,则会被删除并且归档。消息仅会被追加在当前数据文件的末尾,因此速度最快。
-
The Cache
一旦消息写入journal,则被缓存,缓存持续更新消息id的引用以及消息在journal中的位置。这个过程类似checkpoint,一旦引用存储被更新,则消息能够安全的从缓存中移除。通过
checkpoinInterval
配置,或者当缓存达到限制也会主动触发。 -
The Reference Store
journal中消息的引用存储以及消息id的索引存储。可以指定索引储存方式,一种是hash文件,一种是hashmap内存(有数据限制)
AMQ存储目录结构
Activemq启动时会为每个broker创建一个子目录,所以要保证broker的名字唯一。
-
data
directory包含索引和消息引用,如果broker未正常关闭,则在恢复时会删除目录并重建。可以通过在启动broker前删除目录触发强制恢复。
-
state
directorytopic消费者的信息,Journal本身并不保存这些信息,当恢复时会获取这些信息重建数据库。
-
lock
file保证同一时刻仅有一个broker访问此数据。通常用于 stand-by模式。
-
tmp-storage
directory存储非持久化消息。
-
kr-store
AMQ的
referrence
(index),默认使用Kaha
数据库, -
journal
journal的数据文件和 一个
data-control
文件(元数据信息),数据文件通过引用计数,如果所有信息都已消费,则可以删除和归档。 -
achive
仅当
archiving
被启用才创建。journal的数据文件不会被删除,而是移动到此处。当重新放到一个新的journal目录时,会自动重放消息。
AMQ配置
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<broker xmlns="http://activemq.apache.org/schema/core">
<persistenceAdapter>
<amqPersistenceAdapter
directory="target/Broker2-data/activemq-data"
syncOnWrite="true"
indexPageSize="16kb"
indexMaxBinSize="100"
maxFileLength="10mb" />
</persistenceAdapter>
</broker>
</beans>
options:
属性名称 | 默认值 | 注释 |
---|---|---|
directory | activemq-data | 数据及日志存储目录 |
useNIO | true | use NIO to write messages to the data logs |
syncOnWrite | false | 同步写磁盘 |
maxFileLength | 32mb | 文件最大长度 |
persistentIndex | true | 是否持久化index |
maxCheckpointMessageAddSize | 4kb | the maximum number of messages to keep in a transaction before automatically committing |
cleanupInterval | 30000 | time (ms) before checking for a discarding/moving message data logs that are no longer used |
indexBinSize | 1024 | default number of bins used by the index. The bigger the bin size - the better the relative performance of the index |
indexKeySize | 96 | the size of the index key - the key is the message id |
indexPageSize | 16kb | the size of the index page - the bigger the page - the better the write performance of the index |
directoryArchive | archive | the path to the directory to use to store discarded data logs |
archiveDataLogs | false | if true data logs are moved to the archive directory instead of being deleted |
AMQ使用场景
AMQ是默认存储,在易用性和性能之间做了平衡。数据存储嵌入了broker中。它是为可靠性持久化的事务型journal的组合,组合了高性能的索引。
AMQ的可配置性让activemq适用于大部分场景,从高吞吐量应用到大数据量应用。
KahaDB
KahaDB用于突破 AMQ的限制。AMQ为每个index(每个destination都有一个index)使用2个分开的文件,如果Activemq非正常关闭,则可能恢复缓慢。原因是需要broker拷贝所以消息日志重建索引。
KahaDB为所有index适用同一个事物日志文件,并且所有的destination都使用同一个index。
KahaDB的组件很类似AMQ:
- A cache
- Reference Indexes
- A message journal
所有index的更新都记录在一个log文件中。KahaDB使用B-Tree存储结构。
KahaDB配置
<broker brokerName="broker" persistent="true" useShutdownHook="false">
...
<persistenceAdapter>
<kahaDB directory="activemq-data" journalMaxFileLength="32mb"/>
</persistenceAdapter>
...
</broker>
属性:
属性名称 | 默认值 | 注释 |
---|---|---|
archiveCorruptedIndex | false | If true , 启动时归档损坏的index(非删除)。 |
archiveDataLogs | false | If true , 从data log 移动消息到 归档目录,而不是删除 |
checkForCorruptJournalFiles | false | If true , 启动时检查损坏journal日志,并尝试修复。 |
checkpointInterval | 5000 | Time (ms) before check-pointing the journal。checkpoint的间隔时间,默认是5000。 |
checksumJournalFiles | true | 是否为每个消息日志文件提供checksum。在ActiveMQ 5.9.0之前默认值是:false . |
cleanupInterval | 30000 | 在检查到不再使用的消息后,在执行删除消息前的时间(ms),默认30000。 |
compactAcksAfterNoGC | 10 | 从ActiveMQ 5.14.0开始:当启用确认压缩功能时,此值控制必须完成的存储GC周期数,在压缩逻辑被触发之前不会清除其他文件,从而可能将跨日志文件的旧确认压缩到新日志中文件。值设置越低,压缩可能发生得越快,如果经常运行,可能会影响性能。 |
compactAcksIgnoresStoreGrowth | false | 从ActiveMQ 5.14.0开始:当启用确认压缩功能时,此值控制当存储仍在增长时是否运行压缩,或者是否仅在存储停止增长时(由于达到空闲或存储限制而发生)。如果启用,则无论商店仍有空间或处于活动状态,压缩都会运行,这会降低整体性能,但会更快地回收空间。 |
concurrentStoreAndDispatchQueues | true | 是否分发queue消息到客户端,同时进行存储 |
concurrentStoreAndDispatchTopics | false | 是否分发Topic消息到客户端,同时进行存储。 Enabling this property is not recommended. |
directory | activemq-data | The path to the directory to use to store the message store data and log files. |
directoryArchive | null | 定义消息已经被消费过后,移动data log到的路径,默认null。 |
enableAckCompaction | true | 从ActiveMQ 5.14.0:此设置控制商店是否将定期压缩仅包含消息确认的旧日记日志文件。通过将这些旧的确认压缩到新的日志日志文件中,可以删除较旧的文件以释放空间并允许消息存储继续操作而不会达到存储大小限制。 |
enableIndexWriteAsync | false | 是否异步写索引。 |
enableJournalDiskSyncs | true | 是否保证每个没有事务的内容,被同步写入磁盘(JMS持久化的时候需要)。默认为true。 从ActiveMQ 5.14.0: 被 journalDiskSyncStrategy 代替. |
ignoreMissingJournalfiles | false | 是否忽略丢失的消息日志文件 |
indexCacheSize | 10000 | 内存中缓存索引page 的数量 |
indexDirectory | 从ActiveMQ 5.10.0开始,可以单独设置KahaDB index files (db.data and db.redo ) 的存储路径。 | |
indexWriteBatchSize | 1000 | 批量写入磁盘的索引page 数量 |
journalDiskSyncInterval | 1000 | 何时执行磁盘同步的时间间隔(ms) journalDiskSyncStrategy=periodic 。只有在自上次磁盘同步或日志转到新日志文件后对日志进行了写入时,才会执行同步。 |
journalDiskSyncStrategy | always | 从ActiveMQ 5.14.0:此设置配置磁盘同步策略。可用的同步策略列表(按安全性降低和性能提高的顺序):always 确保每个日志写入后跟一个磁盘同步(JMS持久性要求)。这是最安全的选项,但也是最慢的选项,因为它需要在每次写入消息后进行同步。这相当于不推荐使用的属性 enableJournalDiskSyncs=true 。periodic 磁盘将以设定的时间间隔(如果发生写入)而不是在每次日志写入之后同步,这将减少磁盘上的负载并且应该提高吞吐量。滚动到新的日志文件时,磁盘也将同步。默认间隔为1秒。默认间隔提供非常好的性能,同时比更安全 never 磁盘同步,因为数据丢失的最大值限制为1秒。请参阅journalDiskSyncInterval 更改磁盘同步的频率。never 永远不会显式调用同步,并且操作系统将刷新到磁盘。这相当于设置deprecated属性enableJournalDiskSyncs=false 。这是最快的选择,但是最不安全,因为无法确保何时将数据刷新到磁盘。因此,代理失败时可能会发生消息丢失。 |
journalMaxFileLength | `32mb | 每个消息数据文件的最大size。 |
maxAsyncJobs | 10000 | 设置最大的可以存储的异步消息队列,默认值10000,可以和concurrent MessageProducers设置成一样的值。 |
preallocationScope | entire_journal | 从ActiveMQ 5.14.0:此设置配置如何预分配日记帐数据文件。默认策略使用appender线程在首次使用时预先分配日志文件。 entire_journal_async 将在单独的线程中提前使用preallocate。none 禁用预分配。在SSD上,使用 entire_journal_async 避免在首次使用时延迟写入等待预分配。注意:在HDD上,磁盘的额外线程争用会产生负面影响。因此使用默认值。 |
preallocationStrategy | sparse_file | 从ActiveMQ 5.12.0:此设置配置代理在需要新日志文件时尝试预分配日志文件的方式。sparse_file - 设置文件长度,但不会用任何数据填充它。os_kernel_copy - 将预分配委派给操作系统。zeros - 每个预分配的日志文件只包含0x00 整个文件。 |
storeOpenWireVersion | 11 | 确定封送到KahaDB日志的OpenWire命令的版本。在ActiveMQ 5.12.0之前:默认值为6 。代理的某些功能取决于较新协议修订版中存储在OpenWire命令中的信息,如果将商店版本设置为较低值,这些功能可能无法正常工作。在许多情况下,代理版本大于5.9.0的KahaDB存储仍然可以被代理读取,但会导致代理继续使用较旧的商店版本,这意味着较新的功能可能无法按预期工作。对于在ActiveMQ 5.9.0之前的版本中创建的KahaDB存储,需要手动设置storeOpenWireVersion="6" 以便启动代理而不会出现错误。 |
KahaDB使用场景
使用AMQ的场景基本上都可以使用KahaDB,高性能需求或者一个broker内的destination小于等于500,则AMQ更好。
KahaDB不兼容AMQ,也没有工具可转换。仅在新的broker启动时可以选择哪个。
JDBC
默认JDBC driver是Apache Derby。但其他关系型数据库仍然支持。
支持的数据库(未列举完):
-
Apache Derby
-
MySQL
-
PostgreSQL
-
Oracle
-
SQLServer
-
Sybase
-
Informix
-
MaxDB
JDBC Schema
消息存储表(ACTIVEMQ_MSGS)
消息确认表(ACTIVEMQ_ACKS)
JDBC配置
<beans>
<broker brokerName="test-broker" xmlns="http://activemq.apache.org/schema/core">
<persistenceAdapter>
<jdbcPersistenceAdapter dataDirectory="activemq-data" dataSource="#mysql-ds" />
</persistenceAdapter>
</broker>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<broker brokerName="test-broker" xmlns="http://activemq.apache.org/schema/core">
<persistenceAdapter>
<jdbcPersistenceAdapter dataSource="#mysql-ds"/>
</persistenceAdapter>
</broker>
<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://localhost/activemq?relaxAutoCommit=true"/>
<property name="username" value="activemq"/>
<property name="password" value="activemq"/>
<property name="maxActive" value="200"/>
<property name="poolPreparedStatements" value="true"/>
</bean>
</beans>
JDBC使用场景
master/slave模式。
与ActiveMQ Journal一起使用
Memory
Memory配置
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<broker brokerName="test-broker"
persistent="false"
xmlns="http://activemq.apache.org/schema/core">
<transportConnectors>
<transportConnector uri="tcp://localhost:61635"/>
</transportConnectors>
</broker>
</beans>
嵌入式Activemq配置
import org.apache.activemq.broker.BrokerService;
public void createEmbeddedBroker() throws Exception {
BrokerService broker = new BrokerService();
//configure the broker to use the Memory Store
broker.setPersistent(false);
//Add a transport connector
broker.addConnector("tcp://localhost:61616");
//now start the broker
broker.start();
}
Caching Messages in the Broker for Consumers
消息缓存用于
消息缓存工作原理
Activemq为每个topic都缓存消息在内存中,不支持临时topic和通知topic。由于queue的通用操作时hold发送的每条信息,因此消息缓存不适用。
仅当消息分发给topic消费者时,并且消息是可追溯的,永不面向持久topic订阅者,消息才会缓存。
topic消费者通过设置属性consumer.retroactive
来标记是个可追溯的消费。
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.Topic;
public void createRetroactiveConsumer() throws JMSException{
ConnectionFactory fac = new ActiveMQConnectionFactory();
Connection connection = fac.createConnection();
connection.start();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("TEST.TOPIC?consumer.retroactive=true");
MessageConsumer consumer = session.createConsumer(topic);
}
在broker端,消息缓存由Destination Policy
(subscriptionRecoveryPolicy
.)控制,默认策略:FixedSizedSubscriptionRecoveryPolicy
.
Subscription Recovery Policies(订阅恢复策略)
生产者在向某个topic发送了多条消息后,这个时候非持久订阅者才订阅,那么它是不能获取之前生产者发送的信息的。或者,由于网络问题,非持久类型的消费者处于非活跃状态,无法接收到生产者发送的消息。使用消息恢复策略,可以解决上面的问题。ActiveMQ目前支持一个定时或固定大小的恢复缓冲区,在你连接到broker后,在一段时间内的消息会重新发送给订阅者。
Fixed Size Subscription Recovery Policy(固定大小)
可以保留固定字节的消息。
<policyEntry topic=">">
<subscriptionRecoveryPolicy>
<fixedSizedSubscriptionRecoveryPolicy maximumSize="1024"/>
</subscriptionRecoveryPolicy>
</policyEntry>
属性 | 默认值 | 描述 |
---|---|---|
maximumSize | 6553600 | 最大内存(B) |
useSharedBuffer | true | 所有topic共享 |
Fixed Count Subscription Recovery Policy
保留固定数量的消息。
<policyEntry topic=">">
<subscriptionRecoveryPolicy>
<fixedCountSubscriptionRecoveryPolicy maximumSize="100"/>
</subscriptionRecoveryPolicy>
</policyEntry>
属性 | 默认值 | 描述 |
---|---|---|
maximumSize | 100 | 消息数量 |
Query Based Subscription Recovery Policy
根据查询机制使用回溯。
属性 | 默认值 | 描述 |
---|---|---|
query | null | 通过属性查询 |
<policyEntry topic=">">
<subscriptionRecoveryPolicy>
<queryBasedSubscriptionRecoveryPolicy query="Color='red' AND Name='tom'"/>
</subscriptionRecoveryPolicy>
</policyEntry>
Timed Subscription Recovery Policy
保留指定时间内的消息
<policyEntry topic=">">
<subscriptionRecoveryPolicy>
<timedSubscriptionRecoveryPolicy recoverDuration="60000"/>
</subscriptionRecoveryPolicy>
</policyEntry>
属性 | 默认值 | 描述 |
---|---|---|
recoverDuration | 60000 | 最大保留时间(ms) |
Last Image Subscription Recovery Policy
保留最后一条记录
<policyEntry topic=">">
<subscriptionRecoveryPolicy>
<lastImageSubscriptionRecoveryPolicy/>
</subscriptionRecoveryPolicy>
</policyEntry>
No Subscription Recovery Policy
禁用回溯,这是默认配置。
<policyEntry topic=">">
<subscriptionRecoveryPolicy>
<noSubscriptionRecoveryPolicy/>
</subscriptionRecoveryPolicy>
</policyEntry>
RetainedMessageSubscriptionRecoveryPolicy
保留ActiveMQ.Retain属性值为true的最后1条消息。
注意:需要设置retroactive属性为true
<policyEntry topic=">">
<subscriptionRecoveryPolicy>
<retainedMessageSubscriptionRecoveryPolicy/>
</subscriptionRecoveryPolicy>
</policyEntry>
Activemq的安全体系
Basic Security Concepts
Authentication
所有安全概念在Activemq中都是以插件方式实现。Activemq有2种认证插件:
- Simple authentication plugin:在xml直接配置
- JAAS authentication plugin
配置Simple Authentication Plugin
<broker xmlns="http://activemq.org/config/1.0"
brokerName="localhost" dataDirectory="${activemq.base}/data">
<transportConnectors>
<transportConnector name="openwire"
uri="tcp://localhost:61616" />
</transportConnectors>
<plugins>
<simpleAuthenticationPlugin>
<users>
<authenticationUser
username="admin"
password="password"
groups="admins,publishers,consumers"/>
<authenticationUser
username="publisher"
password="password"
groups="publishers,consumers"/>
<authenticationUser
username="consumer"
password="password"
groups="consumers"/>
<authenticationUser
username="guest"
password="password"
groups="guests"/>
</users>
</simpleAuthenticationPlugin>
</plugins>
</broker>
使用凭据
private String username = "publisher";
private String password = "password";
public Publisher() throws JMSException {
factory = new ActiveMQConnectionFactory(brokerURL);
//
connection = factory.createConnection(username, password);
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(null);
}
配置JAAS Plugin
Authorization
Operation Level Authorization
3种操作级别:
- read
- write
- admin
...
<plugins>
<jaasAuthenticationPlugin configuration="activemq-domain" />
<authorizationPlugin>
<map>
<authorizationMap>
<authorizationEntries>
<authorizationEntry topic=">"
read="admins"
write="admins"
admin="admins" />
<authorizationEntry topic="STOCKS.>"
read="consumers"
write="publishers"
admin="publishers" />#
<authorizationEntry topic="STOCKS.ORCL"
read="guests" />
<authorizationEntry topic="ActiveMQ.Advisory.>"
read="admins,publishers,consumers,guests"
write="admins,publishers,consumers,guests"
admin="admins,publishers,consumers,guests" />
</authorizationEntries>
</authorizationMap>
</map>
</authorizationPlugin>
</plugins>
...
Message Level Authorization
控制在消息级别
实现一个MessageAuthorizationPolicy类
public class AuthorizationPolicy implements MessageAuthorizationPolicy {
private static final Log LOG = LogFactory.getLog(AuthorizationPolicy.class);
public boolean isAllowedToConsume (ConnectionContext context, Message message) {
LOG.info(context.getConnection().getRemoteAddress());
if (context.getConnection().getRemoteAddress().startsWith("/127.0.0.1")) {
return true;
} else {
return false;
}
}
}
配置使用此类
<messageAuthorizationPolicy>
<bean class="org.apache.activemq.book.ch5.AuthorizationPolicy" xmlns="" />
</messageAuthorizationPolicy>
Broker Level Operations
自定义Plugin
参考
JAAS:http://java.sun.com/products/jaas/reference/docs/index.html
Login Module:http://java.sun.com/javase/6/docs/api/javax/security/auth/spi/LoginModule.html
discovery:http://activemq.apache.org/discovery-transport-reference.html
peer:http://activemq.apache.org/peer-transport-reference.html
fanout:http://activemq.apache.org/fanout-transport-reference.html
存储:https://activemq.apache.org/persistence
amq:https://activemq.apache.org/amq-message-store
JDBC:https://activemq.apache.org/jdbc-support
TCP :http://activemq.apache.org/tcp-transport-reference.html