上一篇文章中我们说到了失效转移(failover),网络连接器(networkconnection),下面我们就来实践一下即搭建Broker集群,Broker集群的搭建就使用到了failover和networkconnection,如果对failover和networkconnection不了解的可以看看上一篇文章,这里就不重复。
如果应用的访问量不大或者说使用ActiveMQ进行消息发送量不大的则我们可以使用单个消息服务来实现但是如果应用中使用ActiveMQ发送消息的量很大或者说对稳定性、吞吐量要求都很高的话则不能使用单个消息服务了,就要使用集群来实现了。下面看看各种集群方案的改进。
1、第一简单而不太好的设计
方案设计说明:
Broker1 消息服务,接收发送过来的消息。
Broker2和Broker1之间有网络连接器连接,需要的情况下Broker2会从Broker1读取消息。
Broker3和Broker1之间有网络连接器连接,需要的情况下Broker3会从Broker1读取消息。
Broker2和Broker3之间有网络连接器并且是双向的,在需要的情况下可以相互读取消息。
这种方案在不使用failover的情况下,消息路由路径
(1)、P、B1、B2、C
(2)、P、B1、B3、B2、C
这种架构看上去没问题,但这种方式有两个缺点:
(1)、接受消息生产者的消息服务器只有一个,即消息的物理保存点只有一个,如果Broker1出现问题则整个消息发送就玩完了!
(2)、从消息路由的角度来说也不是一个好的设计(访问网络多了不太好慢,路由路劲要控制在一定范围即木桶定律)
这种设计方案的网络连接器配置文件:
端口:
61616 (Broker1)
61617(Broker2)
61618(Broker3)
activemq.xml配置文件配置
Broker1的xml配置:
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
<networkConnectors>
<networkConnector uri="static://(tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)"/>
</networkConnectors>
Broker2和Broker3可以从Broker1读取消息
Broker2的xml配置:
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61617?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
<networkConnectors>
<networkConnector uri="static://(tcp://127.0.0.1:61618)"/>
</networkConnectors>
Broker3可以从Broker2读取消息
Broker3的xml配置:
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61618?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
<networkConnectors>
<networkConnector uri="static://(tcp://127.0.0.1:61617)"/>
</networkConnectors>
Broker2可以从Broker3读取消息。
Broker2和Broker3是相互读取的双向配置可以使用下面这种配置:
在Broker2中这样配置:
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61617?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
<networkConnectors>
<networkConnector uri="static://(tcp://127.0.0.1:61618)" duplex="true"/>
</networkConnectors>
或在Broker3中这样配置:
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61618?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
<networkConnectors>
<networkConnector uri="static://(tcp://127.0.0.1:61617)" duplex="true"/>
</networkConnectors>
其中的duplex="true"就是双向的,所以在Broker2或Broker3中配置一个就ok了!!!
Producer连接工厂:
connectionFactory = new ActiveMQConnectionFactory("admin", "admin","tcp://127.0.0.1:61616");
Consumer连接工厂:
connectionFactory = new ActiveMQConnectionFactory("admin", "admin","tcp://127.0.0.1:61618");
ok 这种设计方式的配置就这些了……
2、比较合理的设计方式
上面的设计方式有两个缺点下面这种设计方式可以说解决了那两个缺点,下面来看看
(1)、数据进行Master-Slave备份,消息消费进行负载均衡,具体如下图(图中的箭头可以理解为消息数据的流向):
Hub1和Hub2进行Master-Slave消息备份
Hub1、Hub2和Broker1、Broker2、Broker3甚至扩展后添加的Broker4、Broker5使用网络连接器连接从而从Hub1/Hub2中获取消息。
这种方案Hub1和Hub2弄成了互备,然后每个Hub都和其他外围的Broker相连,消费者连到Broker1/Broker2/Broker3生产者连到Hub1/Hub2,消息的最长路径不会超过2个Broker,如果以后要扩展比如增加Broker4、Broker5则直接修改Hub1和Hub2增加新的Broker的连接就可以了,不影响消费消息的路由长度,这样所有访问路径的长度都一样,系统就相对稳定一点!
这种方案配置文件:
1)、端口:
activemq:61616(Broker-Hub1)
activemq:61617(Broker-Hub2)
activemq:61618(Broker1)
activemq:61619(Broker2)
activemq:61620(Broker3)
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61618?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
Broker-Hub1的activemq.xml配置文件
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}">
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
<networkConnectors>
<networkConnector uri="static://(tcp://127.0.0.1:61618,tcp://127.0.0.1:61619,tcp://127.0.0.1:61620)"/>
</networkConnectors>
<persistenceFactory>
<journalPersistenceAdapterFactory journalLogFiles="2" journalLogFileSize="16" useJournal="true" useQuickJournal="true" dataSource="#mysql-ds" dataDirectory="${activemq.data}/data"/>
</persistenceFactory>
</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://127.0.0.1:3306/test?relaxAutoCommit=true"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
<property name="poolPreparedStatements" value="true"/>
</bean>
Broker-Hub2的activemq.xml配置文件
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost2" dataDirectory="${activemq.data}">
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61617?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
<networkConnectors>
<networkConnector uri="static://(tcp://127.0.0.1:61618,tcp://127.0.0.1:61619,tcp://127.0.0.1:61620)"/>
</networkConnectors>
<persistenceFactory>
<journalPersistenceAdapterFactory journalLogFiles="2" journalLogFileSize="16" useJournal="true" useQuickJournal="true" dataSource="#mysql-ds" dataDirectory="${activemq.data}/data"/>
</persistenceFactory>
</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://127.0.0.1:3306/test?relaxAutoCommit=true"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
<property name="poolPreparedStatements" value="true"/>
</bean>
ok这样所有Broker的配置都配置完了,下面来看看生产者和消费者的ConnectionFactory的连接:
connectionFactory = new ActiveMQConnectionFactory("admin", "admin","failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617)?randomize=false&priorityBackup=true");
4)、消费者:
connectionFactory = new ActiveMQConnectionFactory("admin", "admin","failover:(tcp://127.0.0.1:61618,tcp://127.0.0.1:61619,tcp://127.0.0.1:61620)?randomize=false&priorityBackup=true");
ok这样上面的这种方案就ok了!
这里说明一下这个方案的图形和个人理解:
1)、这里的Hub1和Hub2是做了Master-Slave主从消息备份,而Hub1/Hub2和Broker1、2、3、4、5之间是使用networkConnection进行单向连接的也就是说Broker从Hub读取消息。
2)、Broker1、Broker2、Broker3之间是没有任何的联系的他们是一个个单独的!
3)、消息生产者,使用了failover(tcp:Hub1,tcp:Hub2)这种方式也就是Master-Slave方式的备份
4)、消息消费者,也是使用了failover这种方式,但在消费者这里使用这种方式可以减小各个Broker的负载,比如Broker1、Broker2、Broker3各个负载一部分而且在某个Broker停止服务后还可以进行转移。
5)、这里强调一点ActiveMQ消息的消费是推拉模式即消费者(Consumer)向消息服务(Broker)注册,当有消息来的时候消息服务器就会把这个消息推送给注册了的消费者。
第二种方案(Hub1、Hub2之间不进行Master-Slave备份而是使用Broker负载),图中的箭头可以理解为消息数据的流向
或者用下面的这张图能更好说明
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost3" dataDirectory="${activemq.data}">
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61618?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
</broker>
2)、Hub-broker1的activemq.xml配置:
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost1" dataDirectory="${activemq.data}">
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
<networkConnectors>
<networkConnector uri="static://(tcp://127.0.0.1:61617,tcp://127.0.0.1:61618,tcp://127.0.0.1:61619,tcp://127.0.0.1:61620)"/>
</networkConnectors>
</broker>
3)、Hub-broker2的activemq.xml配置
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost2" dataDirectory="${activemq.data}">
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61617?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
<networkConnectors>
<networkConnector uri="static://(tcp://127.0.0.1:61616,tcp://127.0.0.1:61618,tcp://127.0.0.1:61619,tcp://127.0.0.1:61620)"/>
</networkConnectors>
</broker>
4)、生产者:
connectionFactory = new ActiveMQConnectionFactory("admin", "admin","failover:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617)?randomize=false&priorityBackup=true");
5)、消费者:
connectionFactory = new ActiveMQConnectionFactory("admin", "admin","failover:(tcp://127.0.0.1:61618,tcp://127.0.0.1:61619,tcp://127.0.0.1:61620)?randomize=false&priorityBackup=true");
当然生产者和消费者配置的ip优先级可以按照自己的需求修改,以实现负载均衡。
这种方式相比上一种方式,数据没有进行主从备份,有可能会导致消息消费的滞后,但是这种方式在消息量大的时候可以实现负载均衡这种能提供更高的性能。
两种方式各有利弊,但网上也看到有人把两种方式整合了的,需要的时候可以看看……
注意:我这篇文章中箭头的指向可以理解为消息数据的流向,所有demo都是测试过的没有问题!
有人可能会问为什么不把Broker1、Broker2、Broker3 进行双向连接,第一他们不是数据的保存点如果Hub1、2出了问题他们都没数据第二那样连了之后路由太长导致消息的获取会很慢!
ok文章到此结束
我的疑惑:
1、在网上看到有的图形中Hub1和Hub2之间进行了Master-Slave数据备份而且还有消息数据的互相读取,这种方式个人已测试过,消息服务根本就起不来即如果做了主从备份则不可能使用网络连接器(networkConnectors)进行连接读取数据。
2、有看到Hub1/Hub2和Broker1、Broker2、Broker3之间是双向连接(网络连接器networkConnectors)个人感觉没必要使用双向连接吧,毕竟不会从Broker读取数据到Hub。
这里写的都是个人的学习达到的程度,如有错误还请指教谢谢!
推荐文章:
http://www.open-open.com/lib/view/open1400126457817.html
http://manzhizhen.iteye.com/blog/2116920
http://www.cnblogs.com/leihenqianshang/articles/5623858.html
http://blog.csdn.net/vtopqx/article/details/51649654