{{ v.name }}
{{ v.cls }}类
{{ v.price }} ¥{{ v.price }}
根据这个系列文章所陈述的中心思想,系统的性能层次包括:代码级性能、规则性能、存储性能、网络性能,以及多节点协同方法(集群方案),所以我们优化activemq的中心思路也是这样的:首先优化activemq单个节点的性能,然后在配置activemq的集群。下面我们就按照这个思路,一步步介绍和activemq性能有关的那些事。
在默认情况下activemq的网络信息传递方式基于网络io模型中的bio方式。那么为了提高activemq单节点的工作性能,我们首先应该为每一个独立的mq服务节点配置更高效的网络io模型。另外我们还需要为activemq考虑一种存储方案,让它能高效的完成“持久化”消息的存储操作。。
使用集群方案能够增加整个软件的性能和稳定性,所以在完成单节点优化以后,我们还需要提供某种集群方案将多个activemq组合起来,让它们协同工作(一定是协同工作,单纯的安装多个activemq节点而不进行协同,是没法提高性能和稳定性的)。
apacheactivemq是开源软件,您可以在activemq的官网下载到它的源代码,并进行代码级别性能的改造,但很明显这并不是一个能获得高效费比的方式。所以笔者不建议您和您的团队更改activemq代码实现(学习一下别人的代码实现还是可以的)。
在上文中,我们提到了activemq支持多种消息协议,包括amqp协议、mqtt协议、openwire协议、stomp协议等。在activemq的官方网站上,列出了目前activemq中支持的所有消息协议,它们是:amqp、mqtt、openwire、rest、stomp、xmpp;
不同的协议需要设置不同的网络监听端口,这个相关设置在activemq安装目录的./conf/conf/activemq.xml主配置文件中。主配置文件采用xml格式进行描述,其中的“transportconnectors”标记描述了各种协议的网络监听端口,示例如下:
以上配置了openwire协议的接入端口号为本机所有ip设备的61616(0.0.0.0代表本机所有ip设备);配置amqp协议的接入端口号为本机所有ip设备的5672;配置stomp协议的接入端口号为本机所有ip设备的61613,等等。这里注意以下几个事实:
每一个“transportconnector”标记的name属性和uri属性都必须填写,name属性的值可以随便填写,它将作为一个connector元素,显示在activemq管理界面的connections栏目中;
每一个“transportconnector”标记的uri元素,都有固定写法:uri头是指定的协议名称,例如amqp、mqtt、stomp等。然后是host/ip域名,指定端口监听所在的路由信息;请不要使用localhost或者127.0.0.1这样的回环地址,否则无法通过网络连接到activemq;接下来是端口信息,指定的端口不能重复,否则会产生冲突。
uri参数部分,每一种协议都有一些特定的参数,读者可参考activemq官网中,关于“协议”部分的介绍:http://activemq.apache.org/protocols.html。但是有些参数却是各种协议都可共用的,例如以上实例中使用的“maximumconnections”属性,代表这个端口支持的最大连接数量;”wireformat.maxframesize”属相代表支持协议的“一个完整消息”的最大数据量(单位为byte);您可以在activemq官网中对wireformats的参数描述中,找到这些默认属性:http://activemq.apache.org/configuring-wire-formats.html。
anytransportwhichinvolvesmarshallingmessagesontosomekindofnetworktransportliketcporudpwilltypicallyusetheopenwireformat.thisisconfigurabletocustomizehowthingsappearonthewire.
uri参数部分,各协议可共用的参数还包括“基本连接特性”相关的参数,这些参数说明可参见官网:http://activemq.apache.org/connection-configuration-uri.html中的详细说明。另外如果您使用的是tcp协议,您还可以在uri参数部分加入tcp相关的属性描述,参见官网http://activemq.apache.org/tcp-transport-reference.html中的详细说明;如果您使用的是udp协议(当然,不推荐这样使用),那么您还可以在uri参数部分加入udp相关的属性描述,参见官网http://activemq.apache.org/udp-transport-reference.html中的详细说明。
“transportconnector”标记中,除了必须填写的“name”属性和“uri”属性以外还有一些可选择的属性,例如:enablestatusmonitor、updateclusterclients。详细的属性介绍可参考官方文档http://activemq.apache.org/configuring-transports.html中“serversideoptions”部分章节的介绍。在后续的文章中,我们将陆续使用到其中的一些设置项
openwireisbinaryprotocoldesignedforworkingwithmessageorientedmiddleware.itisthenativewireformatofactivemq.
openwireisourcrosslanguagewireprotocoltoallownativeaccesstoactivemqfromanumberofdifferentlanguagesandplatforms.thejavaopenwiretransportisthedefaulttransportinactivemq4.xorlater.
packagemq.test.amqp;importjavax.jms.connection;importjavax.jms.destination;importjavax.jms.messageproducer;importjavax.jms.session;importjavax.jms.textmessage;importorg.apache.qpid.amqp_1_0.jms.impl.connectionfactoryimpl;publicclassjmsproducer{publicstaticvoidmain(string[]args)throwsthrowable{//注意,jms-amqp使用的是apacheqpid的实现。如果您需要运行这段代码,请导入qpid的客户端/**
那么各位读者您是否觉得上一小节那样的连接端口配置太过冗长,不好进行管理?确实是这样,并且实际工作中我们也只会使用几种固定的协议。所以activemq在version5.13.0+版本后,将openwire,stomp,amqp,mqtt这四种主要协议的端口监听进行了合并,并使用auto关键字进行表示。也就是说,activemq将监听这一个端口的消息状态,并自动匹配合适的协议格式。配置如下:
以上的uri配置信息中,可以使用所有通用的connectionconfiguration、wireformatsconfiguring、serversideoptions和tcptransportconfiguration配置项。但是这种优化只是让activemq的连接管理变得简洁了,并没有提升单个节点的处理性能。
如果您不特别指定activemq的网络监听端口,那么这些端口都将使用bio网络io模型。所以为了首先提高单节点的网络吞吐性能,我们需要明确指定active的网络io模型,如下所示:
请注意,uri格式头以”nio”开头,表示这个端口使用以tcp协议为基础的nio网络io模型。但是这样的设置方式,只能使这个端口支持openwire协议。那么我们怎么既让这个端口支持nio网络io模型,又让它支持多个协议呢?activemq的服务端设置,允许开发人员使用“+”符号来为端口设置多种特性,如下:
所以如果我们既需要某一个端口支持nio网络io模型,又需要它支持多个协议,那么可以进行如下的配置:
另外,如果是为了生产环境进行的配置,那么您至少应该还要配置这个端口支持的最大连接数量、设置每一条消息的最大传输值、设置nio使用的线程池最大工作线程数量(当然您已经知道了这些设置的文档所在位置,所以您可以根据自己的情况进行设置属性的增减):
由于activemq是jms规范的完整实现,所以为了讲清楚activemq是如何进行存储和调度的,就需要首先说明jms中和存储、调度有关的几个概念。它们是:消息收发模式(订阅-发布和负载均衡模式)、消息存储模式(持久化消息和非持久化消息)和订阅模型(持续订阅和非持续订阅)。
在上篇文章中我们已经详细过订阅-发布模式和负载均衡模式(http://blog.csdn.net/yinwenjie/article/details/50916518#t5)。在activemq的官方文档中的描述,他们的英文名称分别是topics和queue。这两种消息“发送-接受”模式,都是jms规范中的标准模式。为了使各位读者在后续的阅读中,不会和jms中的其他概念不造成混淆,我在后文章中都将使用规范的名称进行称呼。
在“订阅-发布”模式下,消息会被复制多份,分别发送给所有“订阅”者。实际上在后文的描述中您将看到,这个复制的过程实际上没有您想象的简单。
在“负载均衡”模式下,一条消息将会发送给一个消息消费者,如果当前queue没有消息消费者,消息将进行存储。同样通过后文的介绍,您会发现这其中的过程也并不简单。
jms中对非持久化消息和非持久化消息的称呼分别是:non_persistentmessage和persistentmeaage。它们指的是消息在任何一种“发送-接受”模式下(“订阅-发布”模式和“负载均衡模式”),是否进行持久化存储。
non_persistentmessage只存储在jms服务节点的内存区域,不会存储在某种持久化介质上(后面我们要介绍到acitvemq可支持的持久化介质有:kahabd、amq和关系型数据)。在极限情况下,jms服务节点的内存区域不够使用了,也只会采用某种辅助方案进行转存(例如activemq会使用磁盘上的一个“临时存储区域”进行暂存)。一旦jms服务节点宕机了,这些non_persistentmessage就会丢失。
jms中对persistentmeaage的定义是:这些消息不受jms服务端异常状态的影响,jms服务端会使用某种持久化存储方案保存这些消息,直到jms服务端认为这些persistentmeaage被消费端成功处理。例如activemq中可以选择的持久化存储方案就包括:kahadb、amq和关系型数据库。
在jms标准api中,使用setdeliverymode标记消息发送者是发送的persistentmeaage还是non_persistentmessage。示例如下:
......for(intindex=0;index<10;index++){textmessageoutmessage=session.createtextmessage();outmessage.settext("这是发送的消息内容:"+index);if(index%2==0){sender.setdeliverymode(deliverymode.non_persistent);}else{sender.setdeliverymode(deliverymode.persistent);}sender.send(outmessage);}......
那么当jms服务节点重启后(注意不是producer重启),以上代码中发送的10条消息只有其中5条消息能够保存下来。
持续订阅和非持续订阅,是针对“订阅-发布”模式的细分处理策略,在jms规范中的标准称呼是:durable-subscribers和non-durablesubscribers。
durable-subscribers是指在“订阅-发布”模式下,即使标记为durable-subscribers的订阅者下线了(可能是因为订阅者宕机,也可能是因为这个订阅者故意下线),“订阅-发布”模式的topic队列也要保存这些消息(视消息不同的持久化策略影响,保存机制不一样),直到下次这个被标记为durable-subscribers的订阅者重新上线,并正确处理这条消息为止。换句话说,标记为durable-subscribers的订阅者是否能获得某条消息,和它是否曾经下线没有任何关系。