ActiveMQ消息持久化机制详解:JDBC、KahaDB、LevelDB,如何选择?
消息持久化机制,表面上看像是技术方面的细节,然而它却直接对系统是否能够可靠地运行产生影响,一旦在选择的时候出现不恰当的情况,就有可能致使消息发生丢失或者出现性能方面的瓶颈。
JDBC持久化原理
数据库表用于JDBC持久化存储消息,在消息抵达服务器之际,系统会把消息内容、目的地等数据先行写入数据库里的特定表,进而标记成待发送状态,此过程务必创建数据库连接,还要执行SQL插入语句,对数据库性能存在一定要求 。
<persistenceAdapter>
<levelDB directory="activemq-data"/>
</persistenceAdapter>
<!--
Configure message persistence for the broker. The default persistence
mechanism is the KahaDB store (identified by the kahaDB tag).
For more information, see:
http://activemq.apache.org/persistence.html
-->
<!--directory:保存数据的目录;journalMaxFileLength:保存消息的文件大小-->
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb" journalMaxFileLength="32MB"/>
</persistenceAdapter>
当消息成功被投递给消费者之后,系统会去执行删除操作,进而将该消息从数据库里移除掉如果。投递失败了的话消息会继续安留在数据库当中,以便等待那下次的重试。这样的一种机制确保了哪怕是系统进行重新启动,那些未成功投递的消息也并不会出现丢失的情况。
AMQ持久化特点
AMQ采用文件结合方式来存储消息,每个数据文件大小被固定为32MBytes,这样的设计把大量小小消息合并存储于大文件里,进而减少了文件个数,提升了磁盘读写效率,文件依照数字顺序予以命名,像是db-1.log、db-2.log等等!
在所涉及的某个文件之内,当其中所有的消息均被成功消费完毕之后,此文件便会被标记成为可删除的状态。系统会按照定期安排来清理掉这些已经被消费的文件内容,以此来释放磁盘所占用的空间。这样一种机制在消息数量达到十分庞大的程度时,其表现能够保持稳定,从而避免了因海量存在的小文件而致使系统性能出现下降的情况。
KahaDB存储引擎
从ActiveMQ 5.4版本起始,KahaDB变为默认持久化插件,它运用基于日志的文件存储形式,把消息与索引数据分开来存储,此等设计跟现代数据库的存储机制相类似,从而提升了读写效率。
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
只含有四类文件以及一个锁文件的KahaDB的目录结构是简洁明了的,当中db-.log系列文件存放实际消息内容,并且db.data文件运用B-Tree索引实现消息位置的快速定位,这极大地提升了消息检索速度。
<persistenceAdapter>
<jdbcPersistenceAdapter dataSource="#my-ds"/>
</persistenceAdapter>
持久化配置要点
在activemq.xml文件里去修改persistenceAdapter配置,以此进行配置JDBC持久化操作,包括关键参数,其中dataSource指定数据源bean 的那些不同情况,createTablesOnStartup确定是否在启动时创建表 的这一不同情况,通常首次启动时将其设置为true这个不同情况,之后改为false这个不同情况。
处在数据库持久化运用期间,至关重要的事项是一定要保证导入了恰当无误的数据库驱动包,像MySQL这种情况就需要用到mysql-connector-java.jar。与此同时,还得开启持久化方面的设置,不然的话,消息依旧仅仅会被存储于内存里,没办法达成持久化保障。
性能优化策略
在消费者处理速率跟生产者速率达成一致之际,系统能够运用批处理形式把消息写入数据库,进而降低数据库连接的次数,以此提高吞吐量,这样的优化在高峰期尤为有效。
<bean id="mysql-ds" class="org.apache.commons.dbcp2.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="root"/>
<property name="password" value="root"/>
<property name="poolPreparedStatements" value="true"/>
</bean>
要是消费者处理的速度较为迟缓,系统会先把消息写入临时的文件之中,接着以批量的形式同步到数据库里面。这样一种异步处理的机制避开了数据库变成系统的瓶颈,确保了生产者的发送效率不会受到影响。
存储机制对比
核心逻辑保持一致的三种持久化方式分别是,先进行存储进而发送出去,若执行成功便实施删除操作,要是失败了就重新进行尝试,JDBC对适宜跟现有数据库达成集成的场景而言最为合适,AMQ在消息量极为庞大的环境下能够很好地适用,而KahaDB是在性能以及稳定性之间成功获取到了更为出色的平衡 。
当进行持久化方式的选择之际,需要全面地综合考量数据的安全性这一方面,以及系统的性能这另一方面,还有运维成本这第三个方面。对于金融业务而言,有可能会比较倾向于JDBC,然而,至于日志处理系统,或许会更加适配于采用文件存储的方式。
在实际项目里头,你究竟更偏向于挑选哪一种消息持久化方案呢,欢迎来分享一下自身的经验以及观点,要是感觉这篇文章具备帮助作用的话,那就请点赞予以支持吧!
ID:自增的数据库主键
CONTAINER:消息的Destination
MSGID_PROD:消息发送者客户端的主键
MSG_SEQ:是发送消息的顺序,MSGID_PROD+MSG_SEQ可以组成JMS的MessageID
EXPIRATION:消息的过期时间,存储的是从1970-01-01到现在的毫秒数
MSG:消息本体的Java序列化对象的二进制数据
PRIORITY:优先级,从0-9,数值越大优先级越高
CONTAINER:消息的Destination
SUB_DEST:如果是使用Static集群,这个字段会有集群其他系统的信息
CLIENT_ID:每个订阅者都必须有一个唯一的客户端ID用以区分
SUB_NAME:订阅者名称
SELECTOR:选择器,可以选择只消费满足条件的消息。条件可以用自定义属性实现,可支持多属性AND和OR操作
LAST_ACKED_ID:记录消费过的消息的ID。


