ActiveMQ笔记(3):基于Networks of Brokers的HA方案
上一篇介绍了基于ZK的ActiveMQ HA方案,虽然理解起来比较容易,但是有二个不足:
1) 占用的节点数过多,1个zk集群至少3个节点,1个activemq集群也至少得3个节点,但其实正常运行时,只有一个master节点在对外响应,换句话说,花6个节点的成本只为了保证1个activemq master节点的高可用,太浪费资源了。
2) 性能下降太明显,比起单节点的activemq,性能下降了近1个数量级。
这一篇将介绍基于networks of brokers的HA方案,不需要借助zk等第3方组件,只需要2个activemq节点就能达到类似效果,进入正题之前,先来简单谈谈Broker这个概念。
Broker一词的原意是『经纪人、中间人』,用在ActiveMQ的构架中,即:Broker作为Producer与Consumer的中间人(或代理人),生产者不用知道消费者在哪里、如何消费这些细节,只要将消息扔给中间人Broker即可,类似的,消费者也不用关心消息是从哪个生产者过来的,它只知道这是从Broker那里拿来的,如果画一张图来描述,就是下面这样(引用自本文最后参考文章中的图片)

那么,当生产者将消息发给Broker时,会发生什么?下图描述的就是这个过程:

1) 生产者将消息发给Broker
2) Broker将消息落地存储
3) 然后给生产者反馈:事情我已经办妥了!
继续,再来看看消费者又是如何跟Broker打交道的:

1) Broker将接收到的消息,从db中取出
2) 然后发送给消费者
3) 如果消费者使用的是自动确认模式(即:Session.AUTO_ACKNOWLEDGE),则Consumer会马上告诉Broker:ok,消息我已经收到了。
4) 然后进行自己的业务处理
5) Broker一旦收到确认,将会马上更新消息的状态为已消费(或直接删除,取决于持久化的实现机制)(注:虽然图中步骤5排在步骤4之后,但是步骤4、5几乎是同时发生的)
在一些大型应用中,如果一个Broker出现性能瓶颈抗不住压力,可能会拆分成多个Broker,如下图所示:

(注:上图中箭头的方法并非数据流向,而应该理解成调用关系,即:Producer调用Broker1,Consumer调用Broker2...)
Producer将消息发送给Broker1,而Consumer从另一个Broker2接收消息,有点类似数据库读写分离的意思,这样系统的性能可以提升一定程度的提升,但是问题来了,Broker1上的消息,如何"同步"(见下面的注释)到Broker2呢,这就依赖networkConnector的配置。
注:同步这个词用在这里可能不太准确,但也找不到一个更精确的词来描述,实际上,二个broker用上述机制组建成小集群后,如果生产者连接到broker1,消费者连接到broker2,当消息发送到broker1后,broker1不会将该消息复制一份到broker2,而是等消费者从broker2上消费该消息时,这条消息才从broker1取到broker2上,相当于此时broker2是消费者,从broker1消费了一条消息,然后broker2上就有这条消息了,最终消费者才能broker2上拿到这条消息。
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd"> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>file:${activemq.conf}/credentials.properties</value>
</property>
</bean> <broker xmlns="http://activemq.apache.org/schema/core" brokerName="activemq-1">
<networkConnectors>
<networkConnector uri="static:(tcp://127.0.0.1:61626)"/>
</networkConnectors>
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
<transportConnectors>
<transportConnector name="openwire"
uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
</broker> <import resource="jetty.xml"/>
</beans>
注意:14-16行及21-22行,该Broker对外暴露6166端口,同时"连接"到6166端口(即另1个broker),最终的效果相当于,如果有producer把消息发到6166(broker1),则从另一个broker(6166端口)上也能消费这条消息。
明白这些基本原理后,在61626对应的activemq上,也做类似的配置,只不过"连接方向"正好相反,参考以下配置:
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd"> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>file:${activemq.conf}/credentials.properties</value>
</property>
</bean> <broker xmlns="http://activemq.apache.org/schema/core" brokerName="activemq-2">
<networkConnectors>
<networkConnector uri="static:(tcp://127.0.0.1:61616)"/>
</networkConnectors>
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
<transportConnectors>
<transportConnector name="openwire"
uri="tcp://0.0.0.0:61626?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
</broker> <import resource="jetty.xml"/>
</beans>
(注:如果希望2个activemq上都能访问admin管理界面,jetty.xml中的端口要修改,不要冲突)
这样,activemq-1与activemq-2这二个broker就互为主备,发给你的消息会同步到我,发给我的消息也会同步到你,实现了HA,示意图如下:

Producer与Consumer连接到activemq时,配置文件可以这么写:
<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<!--broker服务的地址-->
<property name="brokerURL" value="failover:(tcp://localhost:61616,tcp://localhost:61626)"/>
...
</bean>
</property>
</bean>
这种HA方案的优点是占用的节点数更少(只需要2个节点),而且2个broker都可以响应消息的接收与发送,性能比zookeeper方案要好一些。
最后,再给一个简化配置的技巧,以上述的2节点HA方案中,二个activemq的配置文件都要加networkConnector配置,如果想减轻配置的工作量,也可以只在其中一个activemq设置,参考以下片段:
<networkConnectors>
<networkConnector uri="static:(tcp://127.0.0.1:61626)" duplex="true"/>
</networkConnectors>
即:在61616这个activemq的配置文件中,添加networkConnector时,增加duplex="true",也就是双工通讯的意思,这样61616与61626这二个activemq上的broker就建立了双向通讯连接,另一个activemq上就无需额外配置了(注:如果在61626上配置了,反而会报错)
参考文章:
http://www.jakubkorab.net/2011/11/understanding-activemq-broker-networks.html
http://activemq.apache.org/networks-of-brokers.html
最后贴二段程序运行的输出日志,以说明同步机制的正确性,打消回复中“大鹏520”的顾虑:
背景: *.*.*.15 与 *.*.*.16 作为HA(双主)的activemq集群,发送程序只连接到15发消息,然后退出。 接收程序只从16上收消息,如果收到了,表明15上的消息同步到16。
下面是发送程序的输出片段:(注意输出日志中关于IP的部分,这是只连接到*.*.*.[b]15[/b]上发送的) (注:部分敏感信息,比如真实IP前缀,公司package名,用*代替了)
14:53:09,996 <*.DemoSender> INFO [main]: 准备发送消息...
14:53:10,270 <org.apache.activemq.transport.WireFormatNegotiator> DEBUG [main]: Sending: WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true, SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true, CacheEnabled=true, TightEncodingEnabled=true, MaxFrameSize=9223372036854775807, Host=*.*.*.15, MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000}, magic=[A,c,t,i,v,e,M,Q]}
14:53:10,306 <org.apache.activemq.transport.InactivityMonitor> DEBUG [ActiveMQ Transport: tcp:///*.*.*.:61616@50616]: Using min of local: WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true, SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true, CacheEnabled=true, TightEncodingEnabled=true, MaxFrameSize=9223372036854775807, Host=*.*.*.15, MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000}, magic=[A,c,t,i,v,e,M,Q]} and remote: WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true, SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true, CacheEnabled=true, TightEncodingEnabled=true, MaxFrameSize=104857600, MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000}, magic=[A,c,t,i,v,e,M,Q]}
14:53:10,306 <org.apache.activemq.transport.WireFormatNegotiator> DEBUG [ActiveMQ Transport: tcp:///*.*.*.:61616@50616]: Received WireFormat: WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true, SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true, CacheEnabled=true, TightEncodingEnabled=true, MaxFrameSize=104857600, MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000}, magic=[A,c,t,i,v,e,M,Q]}
14:53:10,306 <org.apache.activemq.transport.WireFormatNegotiator> DEBUG [ActiveMQ Transport: tcp:///*.*.*.:61616@50616]: tcp:///*.*.*.15:61616@50616 before negotiation: OpenWireFormat{version=11, cacheEnabled=false, stackTraceEnabled=false, tightEncodingEnabled=false, sizePrefixDisabled=false, maxFrameSize=9223372036854775807}
14:53:10,307 <org.apache.activemq.transport.WireFormatNegotiator> DEBUG [ActiveMQ Transport: tcp:///*.*.*.:61616@50616]: tcp:///*.*.*.15:61616@50616 after negotiation: OpenWireFormat{version=11, cacheEnabled=true, stackTraceEnabled=true, tightEncodingEnabled=true, sizePrefixDisabled=false, maxFrameSize=104857600}
14:53:10,416 <org.springframework.jms.core.JmsTemplate> DEBUG [main]: Executing callback on JMS Session: PooledSession { ActiveMQSession {id=ID:yangjunmings-MacBook-Pro.local-50615-1461480790088-1:1:1,started=false} java.lang.Object@5a56cdac }
14:53:10,471 <org.springframework.jms.core.JmsTemplate> DEBUG [main]: Sending created message: ActiveMQTextMessage {commandId = 0, responseRequired = false, messageId = null, originalDestination = null, originalTransactionId = null, producerId = null, destination = null, transactionId = null, expiration = 0, timestamp = 0, arrival = 0, brokerInTime = 0, brokerOutTime = 0, correlationId = null, replyTo = null, persistent = false, type = null, priority = 0, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = null, marshalledProperties = null, dataStructure = null, redeliveryCounter = 0, size = 0, properties = null, readOnlyProperties = false, readOnlyBody = false, droppable = false, jmsXGroupFirstForConsumer = false, text = message test:0}
14:53:10,516 <*.DemoSender> INFO [main]: 消息0发送完成!
14:53:11,520 <org.springframework.jms.core.JmsTemplate> DEBUG [main]: Executing callback on JMS Session: PooledSession { ActiveMQSession {id=ID:yangjunmings-MacBook-Pro.local-50615-1461480790088-1:1:1,started=false} java.lang.Object@5a56cdac }
14:53:11,521 <org.springframework.jms.core.JmsTemplate> DEBUG [main]: Sending created message: ActiveMQTextMessage {commandId = 0, responseRequired = false, messageId = null, originalDestination = null, originalTransactionId = null, producerId = null, destination = null, transactionId = null, expiration = 0, timestamp = 0, arrival = 0, brokerInTime = 0, brokerOutTime = 0, correlationId = null, replyTo = null, persistent = false, type = null, priority = 0, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = null, marshalledProperties = null, dataStructure = null, redeliveryCounter = 0, size = 0, properties = null, readOnlyProperties = false, readOnlyBody = false, droppable = false, jmsXGroupFirstForConsumer = false, text = message test:1}
运行完以后,关掉发送程序,然后启动接收程序:
14:54:40,965 <org.apache.activemq.transport.WireFormatNegotiator> DEBUG [main]: Sending: WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true, SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true, CacheEnabled=true, TightEncodingEnabled=true, MaxFrameSize=9223372036854775807, Host=*.*.*.16, MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000}, magic=[A,c,t,i,v,e,M,Q]}
14:54:41,002 <org.apache.activemq.transport.InactivityMonitor> DEBUG [ActiveMQ Transport: tcp:///*.*.*.16:61616@50642]: Using min of local: WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true, SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true, CacheEnabled=true, TightEncodingEnabled=true, MaxFrameSize=9223372036854775807, Host=*.*.*.16, MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000}, magic=[A,c,t,i,v,e,M,Q]} and remote: WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true, SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true, CacheEnabled=true, TightEncodingEnabled=true, MaxFrameSize=104857600, MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000}, magic=[A,c,t,i,v,e,M,Q]}
14:54:41,003 <org.apache.activemq.transport.WireFormatNegotiator> DEBUG [ActiveMQ Transport: tcp:///*.*.*.16:61616@50642]: Received WireFormat: WireFormatInfo { version=11, properties={TcpNoDelayEnabled=true, SizePrefixDisabled=false, CacheSize=1024, StackTraceEnabled=true, CacheEnabled=true, TightEncodingEnabled=true, MaxFrameSize=104857600, MaxInactivityDuration=30000, MaxInactivityDurationInitalDelay=10000}, magic=[A,c,t,i,v,e,M,Q]}
14:54:41,003 <org.apache.activemq.transport.WireFormatNegotiator> DEBUG [ActiveMQ Transport: tcp:///*.*.*.16:61616@50642]: tcp:///*.*.*.16:61616@50642 before negotiation: OpenWireFormat{version=11, cacheEnabled=false, stackTraceEnabled=false, tightEncodingEnabled=false, sizePrefixDisabled=false, maxFrameSize=9223372036854775807}
14:54:41,004 <org.apache.activemq.transport.WireFormatNegotiator> DEBUG [ActiveMQ Transport: tcp:///*.*.*.16:61616@50642]: tcp:///*.*.*.16:61616@50642 after negotiation: OpenWireFormat{version=11, cacheEnabled=true, stackTraceEnabled=true, tightEncodingEnabled=true, sizePrefixDisabled=false, maxFrameSize=104857600}
14:54:41,163 <org.apache.activemq.thread.TaskRunnerFactory> DEBUG [main]: Initialized TaskRunnerFactory[ActiveMQ Session Task] using ExecutorService: java.util.concurrent.ThreadPoolExecutor@485966cc[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
14:54:41,181 <*.DemoListener> INFO [ActiveMQ Session Task-1]: message test:0
注意接收程序中IP的部分,这是从*.*.*.[b]16[/b]上接收到的,说明消息已经从 *.*.*.15同步到*.*.*.16上了,否则不可能收到消息。
ActiveMQ笔记(3):基于Networks of Brokers的HA方案的更多相关文章
- 基于Networks of Brokers的HA方案
上一篇介绍了基于ZK的ActiveMQ HA方案,虽然理解起来比较容易,但是有二个不足: 1) 占用的节点数过多,1个zk集群至少3个节点,1个activemq集群也至少得3个节点,但其实正常运行时 ...
- ActiveMQ笔记(2):基于ZooKeeper的HA方案
activemq官网给出了3种master/slave的HA方案,详见:http://activemq.apache.org/masterslave.html,基于共享文件目录,db,zookeepe ...
- ActiveMQ笔记(4):搭建Broker集群(cluster)
上一篇介绍了基于Networks of Borkers的2节点HA方案,这一篇继续来折腾Networks of Brokers,当应用规模日渐增长时,2节点的broker可能仍然抗不住访问压力,这时候 ...
- ActiveMQ笔记:一个高稳定,可扩展的的部署方案
本文介绍一个笔者在实际工作中的实施的基于ActiveMQ的一个高稳定,可扩展的异步消息系统. ActiveMQ是一个成熟的基于Java语言的开源消息系统,在实际应用中被大量使用.ActiveMQ在系统 ...
- ActiveMQ笔记(7):如何清理无效的延时消息?
ActiveMQ的延时消息是一个让人又爱又恨的功能,具体使用可参考上篇ActiveMQ笔记(6):消息延时投递,在很多需要消息延时投递的业务场景十分有用,但是也有一个缺陷,在一些大访问量的场景,如果瞬 ...
- 【DWT笔记】基于小波变换的降噪技术
[DWT笔记]基于小波变换的降噪技术 一.前言 在现实生活和工作中,噪声无处不在,在许多领域中,如天文.医学图像和计算机视觉方面收集到的数据常常是含有噪声的.噪声可能来自获取数据的过程,也可能来自环境 ...
- RGMII_PHY测试笔记1 基于开发板MiS603-X25
RGMII_PHY测试笔记1 基于开发板MiS603-X25 作者:汤金元 日期:20150817 公司:南京米联电子科技有限公司 博客:http://blog.chinaaet.com/detail ...
- 《软件测试自动化之道》读书笔记 之 基于反射的UI测试
<软件测试自动化之道>读书笔记 之 基于反射的UI测试 2014-09-24 测试自动化程序的任务待测程序测试程序 启动待测程序 设置窗体的属性 获取窗体的属性 设置控件的属性 ...
- 《软件测试自动化之道》读书笔记 之 基于Windows的UI测试
<软件测试自动化之道>读书笔记 之 基于Windows的UI测试 2014-09-25 测试自动化程序的任务待测程序测试程序 启动待测程序 获得待测程序主窗体的句柄 获得有名字控件的 ...
随机推荐
- spring boot(五):spring data jpa的使用
在上篇文章springboot(二):web综合开发中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring data jpa 常见用法以及注意事项 使用spr ...
- AJAX(一)
AJAX(一) Ajax是Asynchronous Javascript和XML的简写,这一技术能够向服务器请求额外的数据而无需卸载页面,会带来更好的用户体验. [前面的基础知识][关于同步和异步的了 ...
- ASP.NET Core官方计划路线及需要废除的一些Framework技术
概述 下面是 ASP.NET Core的时间表和路线图. 注意日期和特性都可能更改. 作为.NET Core这么大的一个项目,很难准确预测每一个计划的是否有变动. 即便如此,我们还是计划公开和透明的实 ...
- 导出BOM表
1.Report->Bill of Materials for Project 将Value拖上左上角的Grouped Columns 2.在Excel表中全选器件,右键设置"设置单元 ...
- C# 3.0新语言特性和改进(一)
引言 关于C#3.0的特性,园子里已经有了一大把,可能大家都很熟悉了,虽然本人开发中使用过,但自己还是需要记录一下,总结一下.同时也是后面写Linq知识的基础.希望有兴趣的朋友,可以看看. C# 3. ...
- ASP.NET Core Kestrel部署HTTPS
ASP.NET Core配置 Kestrel部署HTTPS.现在大部分网站已经部署HTTPS,大家对于安全越来越重视. 今天简单介绍一下ASP.NET Core 部署HTTPS,直接通过配置Kestr ...
- 设计模式(六)原型模式(Prototype Pattern)
一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在 ...
- 关于linux下crontab的使用
minute hour day month dayofweek command minute - 从0到59的整数 hour - 从0到23的整数 day - 从1到31的 ...
- 应用SqlGeometry无法加载sqlserverspatial.dll
最近需要完成一个API,通过用户上传的经纬度判断用户的所在县市省,数据量相对不是很大所以把相关数据全部扔到了内存里知行,主要用到了SqlGeometry, 代码写完后运行本地没问题,扔到服务器上开始报 ...
- node-sass 在Mac中安装报错
在做一个基于react项目要安装依赖的包时总是报一下错误 应该是node-sass的问题 找到官网下载跟我错误提示一样的文件也就是 darwin-x64-46 不同的系统不一样 修改为跟本地一样的名 ...