Activemq-In-action(二)

第一部分 消息系统和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或Queuesend
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)

注意:发送和接受的消息体类型必须保持一一对应。

五中消息体格式

序号消息体类型说明
1TextMessage(文本消息)编码字符串。对于外发消息,字符串在由目标对象给定的字符集中进行编码。缺省情况下使用 UTF8 编码(UTF8 编码从消息的第一个字符开始;开头处无长度字段)。但是,可以指定 用于 JMS 的 IBM® MQ 类 支持的任何其他字符集。此类字符集主要在将消息发送到非 JMS 应用程序时使用。
如果字符集是双字节集(包括 UTF16),那么目标对象的整数编码规范可确定字节顺序。
使用消息本身中指定的字符集及编码来解释入局消息。这些规范在最后一个 IBM MQ 头中,如果没有头,那么在 MQMD 中。对于 JMS 消息,最后一个头通常为 MQRFH2。
2MapMessage(键值对消息)消息体包含了一系列的名字-值对.名字是Strings,而值则是Java primitive 类型.消息体中的条目可以被enumerator按照顺序访问,也可以自由访问.条目的顺序没 有定义.
3ObjectMessage(对象消息)是 Java™ Runtime 以正常方式进行序列化的对象。
4BytesMessage(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。
5StreamMessage(流消息)StreamMessage 与映射消息类似,但无元素名称
Message消息属性
属性说明
JMSXUserID发送消息的用户识别,发送时提供商设置
JMSXappID发送消息的应用标识,发送时提供商设置
JMSXdeliveryCount转发消息重试次数:从1开始,发送方提供商设置
JMSXGroupID消息所在消息组的用户标识,由客户端设置
JMSXGroupSeq组内消息的序号,从1开始.由客户端设置
JMSXProducerTEID产生消息的事务的事务表示,发送方提供商设置
JMSConsumerTXID消费消息的事务的事务表示,接收方提供设置
JMSXRevTimestampJMS转发消息到消费者的事件,接收方提供设置
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>

image-20210519100950796

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 connectordiscoveryUri选项,此选项让其他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

image-20210519102319332

Fanout Protoco

此协议允许客户端同时连接多个broker,并且复制操作到这些broker。

格式:

fanout:(fanoutURI)?key=value

fanoutURI,可以是static URImulticast URI

image-20210519102349873

第5章 消息持久化

Activemq不仅支持JMS规范的数据传递持久化或非持久化模式,还支持数据恢复。

Activemq支持插件策略支持消息存储,以及为in-memory方式提供存储选项:文件模式关系型数据库模式

一旦消息被消费者消费,并且确认Q,就会被删除。

消息存储

queue和topic的存储模式不完全相同,涉及到point-to-pointpublish/subscribe的相关领域

queue采用FIFO模式直接存储,消息只会发给一个consumer,并且消费确认只会,直接删除。

topic模式,每个consumer都可以获取message的副本,一条消息只会存储在一个broker上。订阅者只能接收订阅之后的消息。

image-20210519104247483

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索引以及内存缓存。

image-20210519115217187

AMQ的主要组件:

  • The Journal:

    由消息的滚动日志、命令(事务边界和消息删除)组成,存储在确定长度的文件中。当文件长度达到指定大小,会创建一个新的文件。

    数据文件中的所有消息都有引用计数,一旦数据文件的每条消息都不再被引用,则会被删除并且归档。消息仅会被追加在当前数据文件的末尾,因此速度最快。

  • The Cache

    一旦消息写入journal,则被缓存,缓存持续更新消息id的引用以及消息在journal中的位置。这个过程类似checkpoint,一旦引用存储被更新,则消息能够安全的从缓存中移除。通过checkpoinInterval配置,或者当缓存达到限制也会主动触发。

  • The Reference Store

    journal中消息的引用存储以及消息id的索引存储。可以指定索引储存方式,一种是hash文件,一种是hashmap内存(有数据限制)

AMQ存储目录结构

Activemq启动时会为每个broker创建一个子目录,所以要保证broker的名字唯一。

image-20210519120554483

  • data directory

    包含索引和消息引用,如果broker未正常关闭,则在恢复时会删除目录并重建。可以通过在启动broker前删除目录触发强制恢复。

  • state directory

    topic消费者的信息,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:

属性名称默认值注释
directoryactivemq-data数据及日志存储目录
useNIOtrueuse NIO to write messages to the data logs
syncOnWritefalse同步写磁盘
maxFileLength32mb文件最大长度
persistentIndextrue是否持久化index
maxCheckpointMessageAddSize4kbthe maximum number of messages to keep in a transaction before automatically committing
cleanupInterval30000time (ms) before checking for a discarding/moving message data logs that are no longer used
indexBinSize1024default number of bins used by the index. The bigger the bin size - the better the relative performance of the index
indexKeySize96the size of the index key - the key is the message id
indexPageSize16kbthe size of the index page - the bigger the page - the better the write performance of the index
directoryArchivearchivethe path to the directory to use to store discarded data logs
archiveDataLogsfalseif 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存储结构。

image-20210520123224885

KahaDB配置
<broker brokerName="broker" persistent="true" useShutdownHook="false">
...
  <persistenceAdapter>
 		 <kahaDB directory="activemq-data" journalMaxFileLength="32mb"/>
  </persistenceAdapter>
...
</broker>

属性:

属性名称默认值注释
archiveCorruptedIndexfalseIf true, 启动时归档损坏的index(非删除)。
archiveDataLogsfalseIf true, 从data log 移动消息到 归档目录,而不是删除
checkForCorruptJournalFilesfalseIf true, 启动时检查损坏journal日志,并尝试修复。
checkpointInterval5000Time (ms) before check-pointing the journal。checkpoint的间隔时间,默认是5000。
checksumJournalFilestrue是否为每个消息日志文件提供checksum。在ActiveMQ 5.9.0之前默认值是:false.
cleanupInterval30000在检查到不再使用的消息后,在执行删除消息前的时间(ms),默认30000。
compactAcksAfterNoGC10ActiveMQ 5.14.0开始:当启用确认压缩功能时,此值控制必须完成的存储GC周期数,在压缩逻辑被触发之前不会清除其他文件,从而可能将跨日志文件的旧确认压缩到新日志中文件。值设置越低,压缩可能发生得越快,如果经常运行,可能会影响性能。
compactAcksIgnoresStoreGrowthfalseActiveMQ 5.14.0开始:当启用确认压缩功能时,此值控制当存储仍在增长时是否运行压缩,或者是否仅在存储停止增长时(由于达到空闲或存储限制而发生)。如果启用,则无论商店仍有空间或处于活动状态,压缩都会运行,这会降低整体性能,但会更快地回收空间。
concurrentStoreAndDispatchQueuestrue是否分发queue消息到客户端,同时进行存储
concurrentStoreAndDispatchTopicsfalse是否分发Topic消息到客户端,同时进行存储。 Enabling this property is not recommended.
directoryactivemq-dataThe path to the directory to use to store the message store data and log files.
directoryArchivenull定义消息已经被消费过后,移动data log到的路径,默认null。
enableAckCompactiontrueActiveMQ 5.14.0:此设置控制商店是否将定期压缩仅包含消息确认的旧日记日志文件。通过将这些旧的确认压缩到新的日志日志文件中,可以删除较旧的文件以释放空间并允许消息存储继续操作而不会达到存储大小限制。
enableIndexWriteAsyncfalse是否异步写索引。
enableJournalDiskSyncstrue是否保证每个没有事务的内容,被同步写入磁盘(JMS持久化的时候需要)。默认为true。 从ActiveMQ 5.14.0: 被 journalDiskSyncStrategy代替.
ignoreMissingJournalfilesfalse是否忽略丢失的消息日志文件
indexCacheSize10000内存中缓存索引page的数量
indexDirectoryActiveMQ 5.10.0开始,可以单独设置KahaDB index files (db.data and db.redo) 的存储路径。
indexWriteBatchSize1000批量写入磁盘的索引page数量
journalDiskSyncInterval1000何时执行磁盘同步的时间间隔(ms) journalDiskSyncStrategy=periodic。只有在自上次磁盘同步或日志转到新日志文件后对日志进行了写入时,才会执行同步。
journalDiskSyncStrategyalwaysActiveMQ 5.14.0:此设置配置磁盘同步策略。可用的同步策略列表(按安全性降低和性能提高的顺序):always确保每个日志写入后跟一个磁盘同步(JMS持久性要求)。这是最安全的选项,但也是最慢的选项,因为它需要在每次写入消息后进行同步。这相当于不推荐使用的属性 enableJournalDiskSyncs=trueperiodic磁盘将以设定的时间间隔(如果发生写入)而不是在每次日志写入之后同步,这将减少磁盘上的负载并且应该提高吞吐量。滚动到新的日志文件时,磁盘也将同步。默认间隔为1秒。默认间隔提供非常好的性能,同时比更安全 never磁盘同步,因为数据丢失的最大值限制为1秒。请参阅journalDiskSyncInterval更改磁盘同步的频率。never永远不会显式调用同步,并且操作系统将刷新到磁盘。这相当于设置deprecated属性enableJournalDiskSyncs=false。这是最快的选择,但是最不安全,因为无法确保何时将数据刷新到磁盘。因此,代理失败时可能会发生消息丢失。
journalMaxFileLength`32mb每个消息数据文件的最大size。
maxAsyncJobs10000设置最大的可以存储的异步消息队列,默认值10000,可以和concurrent MessageProducers设置成一样的值。
preallocationScopeentire_journalActiveMQ 5.14.0:此设置配置如何预分配日记帐数据文件。默认策略使用appender线程在首次使用时预先分配日志文件。 entire_journal_async将在单独的线程中提前使用preallocate。none禁用预分配。在SSD上,使用 entire_journal_async避免在首次使用时延迟写入等待预分配。注意:在HDD上,磁盘的额外线程争用会产生负面影响。因此使用默认值。
preallocationStrategysparse_fileActiveMQ 5.12.0:此设置配置代理在需要新日志文件时尝试预分配日志文件的方式。sparse_file - 设置文件长度,但不会用任何数据填充它。os_kernel_copy - 将预分配委派给操作系统。zeros - 每个预分配的日志文件只包含0x00整个文件。
storeOpenWireVersion11确定封送到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 PolicysubscriptionRecoveryPolicy.)控制,默认策略:FixedSizedSubscriptionRecoveryPolicy.

Subscription Recovery Policies(订阅恢复策略)

生产者在向某个topic发送了多条消息后,这个时候非持久订阅者才订阅,那么它是不能获取之前生产者发送的信息的。或者,由于网络问题,非持久类型的消费者处于非活跃状态,无法接收到生产者发送的消息。使用消息恢复策略,可以解决上面的问题。ActiveMQ目前支持一个定时或固定大小的恢复缓冲区,在你连接到broker后,在一段时间内的消息会重新发送给订阅者。

Fixed Size Subscription Recovery Policy(固定大小)

可以保留固定字节的消息。

<policyEntry topic=">">
	<subscriptionRecoveryPolicy>
		<fixedSizedSubscriptionRecoveryPolicy maximumSize="1024"/>
	</subscriptionRecoveryPolicy>
</policyEntry>

属性默认值描述
maximumSize6553600最大内存(B)
useSharedBuffertrue所有topic共享
Fixed Count Subscription Recovery Policy

保留固定数量的消息。

<policyEntry topic=">">
	<subscriptionRecoveryPolicy>
		<fixedCountSubscriptionRecoveryPolicy maximumSize="100"/>
	</subscriptionRecoveryPolicy>
</policyEntry>
属性默认值描述
maximumSize100消息数量
Query Based Subscription Recovery Policy

根据查询机制使用回溯。

属性默认值描述
querynull通过属性查询
<policyEntry topic=">">
	<subscriptionRecoveryPolicy>
		<queryBasedSubscriptionRecoveryPolicy query="Color='red' AND Name='tom'"/>
	</subscriptionRecoveryPolicy>
</policyEntry>

Timed Subscription Recovery Policy

保留指定时间内的消息

<policyEntry topic=">">
	<subscriptionRecoveryPolicy>
		<timedSubscriptionRecoveryPolicy recoverDuration="60000"/>
	</subscriptionRecoveryPolicy>
</policyEntry>
属性默认值描述
recoverDuration60000最大保留时间(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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值