ActiveMQ的安装
安装要求:
部署需要jdk1.5及以上,编译需要jdk1.5(java5)及以上
Java的环境变量(JAVA_HOME)必须设置,即jdk安装的目录,比如c:\Program Files\jsdk.1.6
下载ActiveMQ:http://activemq.apache.org/download.html
解压,如图:
运行bin文件夹下的activemq.bat,出现如下图所示:
验证是否运行成功:
在浏览器中输入:http://localhost:8161/admin/,出现如下图所示表示成功:
此时,ActiveMQ已经安装完成了,接下来配置登录监视控制台的用户名和密码。
打开conf文件夹下的jetty.xml,找到
把authenticate属性的值改成true即可,重启activemq.bat,再登录监视控制台,就需要输入密码了,默认的用户名和密码是admin/admin。roles属性指的是登录的用户角色,这些登录的用户在jetty-realm.properties配置。
修改web的访问端口,在jetty.xml找到一下配置,修改8161即可。
ActiveMQ的运行日志存放在data文件夹下的activemq.log中。
Linux和Aix系统下的安装:
解压:tar zxvf activemq-x.x.x.tar.gz,进入bin文件夹,运行:./activemq start &,也可以只运行:./activemq console。
验证方式和安全性配置和windows下的配置一样。
ActiveMQ消息持久化
在broker中设置属性persistent=”true”(默认是true),同时发送的消息也应该是persitent类型的。ActiveMQ消息持久化有三种方式:AMQ、KahaDB、JDBC。
1、AMQ
AMQ是一种文件存储形式,它具有写入速度快和容易恢复的特点。消息存储在一个个文件中,文件的默认大小为32兆,如果一条消息的大小超过了32兆,那么这个值必须设置大点。当一个存储文件中的消息已经全部被消费,那么这个文件将被标识为可删除,在下一个清除阶段,这个文件被删除。默认配置如下:
AMQ的属性:
属性名称 | 默认值 | 描述 |
directory | activemq-data | 消息文件和日志的存储目录 |
useNIO | true | 使用NIO协议存储消息 |
syncOnWrite | false | 同步写到磁盘,这个选项对性能影响非常大 |
maxFileLength | 32mb | 一个消息文件的大小 |
persistentIndex | true | 消息索引的持久化,如果为false,那么索引保存在内存中 |
maxCheckpointMessageAddSize | 4kb | 一个事务允许的最大消息量 |
cleanupInterval | 30000 | 清除操作周期,单位ms |
indexBinSize | 1024 | 索引文件缓存页面数,缺省为1024,当amq扩充或者缩减存储时,会锁定整个broker,导致一定时间的阻塞,所以这个值应该调整到比较大,但是代码中实现会动态伸缩,调整效果并不理想。 |
indexKeySize | 96 | 索引key的大小,key是消息ID |
indexPageSize | 16kb | 索引的页大小 |
directoryArchive | archive | 存储被归档的消息文件目录 |
archiveDataLogs | false | 当为true时,归档的消息文件被移到directoryArchive,而不是直接删除 |
2、KahaDB
KahaDB是基于文件的本地数据库储存形式,虽然没有AMQ的速度快,但是它具有强扩展性,恢复的时间比AMQ短,从5.4版本之后KahaDB做为默认的持久化方式。默认配置如下:
KahaDB的属性:
property name | default value | Comments |
directory | activemq-data | 消息文件和日志的存储目录 |
indexWriteBatchSize | 1000 | 一批索引的大小,当要更新的索引量到达这个值时,更新到消息文件中 |
indexCacheSize | 10000 | 内存中,索引的页大小 |
enableIndexWriteAsync | false | 索引是否异步写到消息文件中 |
journalMaxFileLength | 32mb | 一个消息文件的大小 |
enableJournalDiskSyncs | true | 是否讲非事务的消息同步写入到磁盘 |
cleanupInterval | 30000 | 清除操作周期,单位ms |
checkpointInterval | 5000 | 索引写入到消息文件的周期,单位ms |
ignoreMissingJournalfiles | false | 忽略丢失的消息文件,false,当丢失了消息文件,启动异常 |
checkForCorruptJournalFiles | false | 检查消息文件是否损坏,true,检查发现损坏会尝试修复 |
checksumJournalFiles | false | 产生一个checksum,以便能够检测journal文件是否损坏。 |
5.4版本之后有效的属性: | ||
archiveDataLogs | false | 当为true时,归档的消息文件被移到directoryArchive,而不是直接删除 |
directoryArchive | null | 存储被归档的消息文件目录 |
databaseLockedWaitDelay | 10000 | 在使用负载时,等待获得文件锁的延迟时间,单位ms |
maxAsyncJobs | 10000 | 同个生产者产生等待写入的异步消息最大量 |
concurrentStoreAndDispatchTopics | false | 当写入消息的时候,是否转发主题消息 |
concurrentStoreAndDispatchQueues | true | 当写入消息的时候,是否转发队列消息 |
5.6版本之后有效的属性: | ||
archiveCorruptedIndex | false | 是否归档错误的索引 |
从5.6版本之后,有可能发布通过多个kahadb持久适配器来实现分布式目标队列存储。什么时候用呢?如果有一个快速的生产者和消费者,当某一个时刻生产者发生了不规范的消费,那么有可能产生一条消息被存储在两个消息文件中,同时,有些目标队列是危险的并且要求访问磁盘。在这种情况下,你应该用通配符来使用mKahaDB。如果目标队列是分布的,事务是可以跨越多个消息文件的。
每个KahaDB的实例都可以配置单独的适配器,如果没有目标队列提交给filteredKahaDB,那么意味着对所有的队列有效。如果一个队列没有对应的适配器,那么将会抛出一个异常。配置如下:
如果filteredKahaDB的perDestination属性设置为true,那么匹配的目标队列将会得到自己对应的KahaDB实例。配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
<persistenceAdapter>
<
mKahaDB
directory
=
"${activemq.base}/data/kahadb"
>
<filteredPersistenceAdapters>
<
!
--
kahaDB
per
destinations
--
>
<
filteredKahaDB
perDestination
=
"true"
>
<persistenceAdapter>
<
kahaDB
journalMaxFileLength
=
"32mb"
/
>
<
/
persistenceAdapter
>
<
/
filteredKahaDB
>
<
/
filteredPersistenceAdapters
>
<
/
mKahaDB
>
<
/
persistenceAdapter
>
|
3、JDBC
配置JDBC适配器:
dataSource指定持久化数据库的bean,createTablesOnStartup是否在启动的时候创建数据表,默认值是true,这样每次启动都会去创建数据表了,一般是第一次启动的时候设置为true,之后改成false。
MYSQL持久化bean
SQL Server持久化bean
Oracle持久化bean
DB2持久化bean
1
2
3
4
5
6
7
8
|
<
bean
id
=
"db2-ds"
class
=
"org.apache.commons.dbcp.BasicDataSource"
destroy
-
method
=
"close"
>
<
property
name
=
"driverClassName"
value
=
"com.ibm.db2.jcc.DB2Driver"
/
>
<
property
name
=
"url"
value
=
"jdbc:db2://hndb02.bf.ctc.com:50002/activemq"
/
>
<
property
name
=
"username"
value
=
"activemq"
/
>
<
property
name
=
"password"
value
=
"activemq"
/
>
<
property
name
=
"maxActive"
value
=
"200"
/
>
<
property
name
=
"poolPreparedStatements"
value
=
"true"
/
>
<
/
bean
>
|
ActiveMQ消息发送与接收
配置完了持久化之后,我们就可以使用代码来发送和接收ActiveMQ中的消息了,我这里配置的持久化是KahaDB。
需要导入的jar包:
一段发送消息的代码:
执行了上面的发送方法之后,在ActiveMQ的监视控制可以看到有一个test队列,并且有一条消息,如图:
点击队列名test,然后点击消息ID即可查看消息内容,如图:
如果DeliveryMode没有设置或者设置为NON_PERSISTENT,那么重启MQ之后消息就会丢失。
一段接收消息的代码:
执行了上面的接收方法之后,在ActiveMQ的监视控制可以看到test队列的消息已经被消费了,如图:
这里的代码只是测试用,在正式开发中一般与Spring结合使用jmsTemplate来发送消息,现实JMS的MessageListener来监听消息。
ActiveMQ配置安全性
监视ActiveMQ的方式有多种,在第一部分中已经说到了Web监视控制台,设置登录用户名和密码,这里再说一下JMX监控。运行了ActiveMQ之后,再运行jdk自带的jconsole即可以看到ActiveMQ的进程,如图:点击连接之后就可以看到ActiveMQ的运行情况。默认情况下是不需要用户名和口令的,修改activemq.bat,找到
修改成
Linux下的找到:
去掉注释即可。
重启ActiveMQ之后,在用jconsole连接就需要输入用户名和密码,jmx.access文件配置用户的访问权限readonly和readwrite,admin readwrite表示用户admin具有读写权限。Jmx.password文件配置用户的密码,admin activemq 表示admin用户的密码是activemq。
除了监视台可以设置用户名和密码之后,ActiveMQ也可以对各个主题和队列设置用户名和密码,配置如下:
simpleAuthenticationPlugin中设置用户名、密码和群组,authorizationPlugin设置主题和队列的访问群组,“>”表示所有的主题或者队列。上面的配置中添加了一个testUser,属于群组testGroup,同时设置test这个队列的访问读写权限为testGroup,当然admins也可以访问的,因为admins是对所有的队列都有访问权限。将第三部分代码中的设置用户名和密码改成刚刚添加的用户testUser,如果密码不正确,将会抛出User name or password is invalid.异常,如果testUser所属的群组不能访问test队列,那么会抛出User guest is not authorized to write to: queue://test异常。需要注意的是所有的群组都需要对以ActiveMQ.Advisory为前缀的主题具有访问权限。
ActiveMQ负载均衡
ActiveMQ可以实现多个mq之间进行路由,假设有两个mq,分别为brokerA和brokerB,当有一条消息发送到brokerA的队列test中,有一个客户端连接到brokerB上,并且要求获取test队列的消息时,brokerA中队列test的消息就会路由到brokerB上,反之brokerB的消息也会路由到brokerA。
静态路由配置,brokerA不需要特别的配置,brokerB需要配置networkConnectors节点,具体配置如下:
静态路由支持failover,如:static:failover://(tcp://host1:61616,tcp://host2:61616)。
动态路由配置,每个mq都需要配置如下:
注意:networkConnectors需要配置在persistenceAdapter之前。
重启ActiveMQ,可以看到brokerA的日志如图:
networkConnector的属性请参照:http://activemq.apache.org/networks-of-brokers.html
ActiveMQ主备配置
ActiveMQ的主备有三种方式:纯Master/Slave、文件共享方式、数据库共享方式。
1、纯Master/Slave
这种方式的主备不需要对Master Broker做特殊的配置,只要在Slave Broker中指定他的Master就可以了,指定Master有两种方式,最简单的配置就是在broker节点中添加masterConnectorURI=”tcp://localhost:61616″即可,还有一种方式就是添加一个services节点,可以指定连接的用户名和密码,配置如下:
纯Master/Slave只允许一个Slave连接到Master上面,也就是说只能有2台MQ做集群,同时当Master挂了之后需要停止Slave来恢复负载。
2、数据库共享方式
这种方式的主备采用数据库做消息的持久化,支持多个Slave,所有broker持久化数据源配置成同一个数据源,当一个broker获取的数据库锁之后,其他的broker都成为slave并且等待获取锁,当master挂了之后,其中的一个slave将会立刻获得数据库锁成为master,重启之前挂掉的master之后,这个master也就成了slave,不需要停止slave来恢复。由于采用的是数据库做为持久化,它的性能是有限的。
3、文件共享方式
这种方式的主备具有和数据库共享方式的负载一样的特性,不同的是broker的持久化采用的是文件(我这里用KahaDB),slave等待获取的锁是文件锁,它具有更高的性能,但是需要文件共享系统的支持。
Window下共享KahaDB持久化的目录,配置如下:
Linux下需要开启NFS服务,具体操作如下:
创建共享目录(192.168.0.1):
1、 修改etc/exports,添加需要共享的目录:/opt/mq/data *(rw,no_root_squash)
2、 启动NFS服务 service nfs start/restart
3、 查看共享 showmount –e
4、 NFS服务自启动 chkconfig –level 35 nfs on
挂载共享目录(192.168.0.2):
1、 挂载:mount –t nfs 192.168.0.1:/opt/mq/data /opt/mq/data
2、 启动自动挂载:在etc/fstab文件添加10.175.40.244:/opt/mq/data /opt/mq/data nfs defaults 0 0
然后指定KahaDB的持久化目录为/opt/mq/data即可。
AIX系统的文件共享和Linux类似,也是启动NFS服务。
注意:如果Master服务器宕机了,Slave是不会获得文件锁而启动,直到Master服务器重启。
Window下Master上有Slave连接时如图:
客户端连接的brokerURL为failover:(tcp://localhost:61616,tcp://localhost:61617)。用第三部分的代码测试,先向Master Broker发送一个消息,然后关闭master,运行获取消息的方法,即可获取之前发送的消息。
ActiveMQ性能优化
1、目标策略
在节点destinationPolicy配置策略,可以对单个或者所有的主题和队列进行设置,使用流量监控,当消息达到memoryLimit的时候,ActiveMQ会减慢消息的产生甚至阻塞,destinationPolicy的配置如下:
producerFlowControl表示是否监控流量,默认为true,如果设置为false,消息就会存在磁盘中以防止内存溢出;memoryLimit表示在producerFlowControl=”true”的情况下,消息存储在内存中最大量,当消息达到这个值时,ActiveMQ会减慢消息的产生甚至阻塞。policyEntry的属性参考:http://activemq.apache.org/per-destination-policies.html
当producer发送的持久化消息到达broker之后,broker首先会把它保存在持久存储中。接下来,如果发现当前有活跃的consumer,如果这个consumer消费消息的速度能跟上producer生产消息的速度,那么ActiveMQ会直接把消息传递给broker内部跟这个consumer关联的dispatch queue;如果当前没有活跃的consumer或者consumer消费消息的速度跟不上producer生产消息的速度,那么ActiveMQ会使用Pending Message Cursors保存对消息的引用。在需要的时候,Pending Message Cursors把消息引用传递给broker内部跟这个consumer关联的dispatch queue。以下是两种Pending Message Cursors:
VM Cursor:在内存中保存消息的引用。
File Cursor:首先在内存中保存消息的引用,如果内存使用量达到上限,那么会把消息引用保存到临时文件中。
在缺省情况下,ActiveMQ 会根据使用的Message Store来决定使用何种类型的Message Cursors,但是你可以根据destination来配置Message Cursors。
对于topic,可以使用的pendingSubscriberPolicy 有vmCursor和fileCursor。可以使用的PendingDurableSubscriberMessageStoragePolicy有
vmDurableCursor 和 fileDurableSubscriberCursor;对于queue,可以使用的pendingQueuePolicy有vmQueueCursor 和 fileQueueCursor。
Message Cursors的使用参考:http://activemq.apache.org/message-cursors.html
2、存储设置
设置消息在内存、磁盘中存储的大小,配置如下:
memoryUsage表示ActiveMQ使用的内存,这个值要大于等于destinationPolicy中设置的所有队列的内存之和。
storeUsage表示持久化存储文件的大小。
tempUsage表示非持久化消息存储的临时内存大小。
使用过程中出现的问题
报错日志:Usage Manager Store is Full, 100% of 1073741824. Stopping producer (ID:db01-48754-1336034955132-0:5:1:1) to prevent flooding queue://queue.land.group. See http://activemq.apache.org/producer-flow-control.html for more info (blocking for: 1s) | org.apache.activemq.broker.region.Queue | ActiveMQ Transport: tcp:///172.24.99.41:44716
这是由于我们在配置文件中设置了storeUsage ,当存储的消息文件(log文件)超过了这值就会报这个异常,在官方网站看到说消息文件不删除是5.3版本的一个bug,在5.5版本的时候已经被解决了,但是我们使用的是5.5.1版本啊,然后在看存储下来的消息文件,文件名不是连续的,那么说明其中还是有被删除的,后来在评论中看到Jeff Genender 说的这个可能是ActiveMQ的线程调度问题,只要不使用线程调度就可以了,在broker中设置属性schedulerSupport=”false” ,这样消息文件就会自动在cleanup阶段删除了。