总的来说broker的集群就两种,主从集群(master-slave)和多主集群(network of brokers),看到有网文将apache activemq的官网的文档中clustering一节中的Queue comsumer cluster当成一种集群部署方式,其实仔细理解文档意思,这种集群是cusumer的集群,并不是我们通常在实际生产环境中用到的broker集群,所以这里声明一下本文提到的activemq的集群都是broker的集群。
前面讲broker与broker之间的通讯也提到了,broker与broker之间的网络桥接是通过network包下面的类来完成的。
Master-Slave方式
一共有三种方式:(1)基于共享文件系统,(2)基于共享JDBC,(3)基于可复制LevelDB(依赖zookeeper),此处仅讲解第一种方式的配置。
persistenceAdapter的配置:
<persistenceAdapter>
<kahaDB directory="D:/MQ/apache-activemq/cluster/kahadb"/>
</persistenceAdapter>
transportConnector的配置:
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61617?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
Broker-Cluster方式
一共有两种方法:(1)静态发现方式 (2)动态发现方式
静态发现
broker1的配置
<networkConnectors>
<networkConnector uri="static:(tcp:// 0.0.0.0:61617)"duplex="false"/>
</networkConnectors>
<transportConnectors>
<transportConnector name="openwire"uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
broker2的配置
<networkConnectors>
<networkConnector uri="static:(tcp:// 0.0.0.0:61616)"duplex="false"/>
</networkConnectors>
<transportConnectors>
<transportConnector name="openwire"uri="tcp://0.0.0.0:61617?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
动态发现
broker1的配置
<networkConnectors>
<networkConnectoruri="multicast://default" dynamicOnly="true" networkTTL="3" prefetchSize="1" decreaseNetworkConsumerPriority="true" />
</networkConnectors>
<transportConnectors>
<transportConnectorname="openwire"uri="tcp://0.0.0.0:61616? " discoveryUri="multicast://default"/>
</transportConnectors>
broker2的配置
<networkConnectors>
<networkConnectoruri="multicast://default" dynamicOnly="true" networkTTL="3" prefetchSize="1" decreaseNetworkConsumerPriority="true" />
</networkConnectors>
<transportConnectors>
<transportConnectorname="openwire"uri="tcp://0.0.0.0:61617" discoveryUri="multicast://default"/>
</transportConnectors>
实际应用中,比如电商的架构中,是混合式部署消息中间件的,即多个master通过network of broker连接,每个master又与多个slaver连接,这样可以大大的提高消息中间件的可用性,这里不作赘述。
附加一段networkconnector的连接远程broker的代码,activemq默认使用DiscoveryNetworkConnector.
先看一下SimpleDiscoveryAgent类中的start方法:
public void start() throws Exception {
taskRunner = new TaskRunnerFactory();
taskRunner.init();
running.set(true);
for (int i = 0; i < services.length; i++) {
//这里触发了networkconnector的add service事件,这里的service就是个uri
listener.onServiceAdd(new SimpleDiscoveryEvent(services[i]));
}
}
然后我们看看DiscoveryNetworkConnector的事件处理函数onServiceAdd
public void onServiceAdd(DiscoveryEvent event) {
// Ignore events once we start stopping.
if (serviceSupport.isStopped() || serviceSupport.isStopping()) {
return;
}
//获取url
String url = event.getServiceName();
if (url != null) {
URI uri;
try {
uri = new URI(url);
} catch (URISyntaxException e) {
LOG.warn("Could not connect to remote URI: {} due to bad URI syntax: ", url, e);
return;
}
//如果和本机地址一样直接返回(显然配错了)
if (localURI.equals(uri)) {
LOG.debug("not connecting loopback: {}", uri);
return;
}
if (connectionFilter != null && !connectionFilter.connectTo(uri)) {
LOG.debug("connectionFilter disallows connection to: {}", uri);
return;
}
// Should we try to connect to that URI?
if (activeEvents.putIfAbsent(uri, event) != null) {
LOG.debug("Discovery agent generated a duplicate onServiceAdd event for: {}", uri);
return;
}
URI connectUri = uri;
try {
connectUri = URISupport.applyParameters(connectUri, parameters, DISCOVERED_OPTION_PREFIX);
} catch (URISyntaxException e) {
LOG.warn("could not apply query parameters: {} to: {}", new Object[]{ parameters, connectUri }, e);
}
LOG.info("Establishing network connection from {} to {}", localURI, connectUri);
Transport remoteTransport;
Transport localTransport;
try {
// Allows the transport to access the broker's ssl configuration.
SslContext.setCurrentSslContext(getBrokerService().getSslContext());
try {
//连接远程broker
remoteTransport = TransportFactory.connect(connectUri);
} catch (Exception e) {
LOG.warn("Could not connect to remote URI: {}: {}", connectUri, e.getMessage());
LOG.debug("Connection failure exception: ", e);
activeEvents.remove(uri);
return;
}
try {
//获取本地transport信息
localTransport = createLocalTransport();
} catch (Exception e) {
ServiceSupport.dispose(remoteTransport);
LOG.warn("Could not connect to local URI: {}: {}", localURI, e.getMessage());
LOG.debug("Connection failure exception: ", e);
activeEvents.remove(uri);
return;
}
} finally {
SslContext.setCurrentSslContext(null);
}
//建立桥接,管理本机broker到远程broker的连接
NetworkBridge bridge = createBridge(localTransport, remoteTransport, event);
try {
synchronized (bridges) {
//根据地址来保存桥接信息
bridges.put(uri, bridge);
}
bridge.start();
} catch (Exception e) {
ServiceSupport.dispose(localTransport);
ServiceSupport.dispose(remoteTransport);
LOG.warn("Could not start network bridge between: {} and: {} due to: {}", new Object[]{ localURI, uri, e.getMessage() });
LOG.debug("Start failure exception: ", e);
try {
// Will remove bridge and active event.
discoveryAgent.serviceFailed(event);
} catch (IOException e1) {
LOG.debug("Discovery agent failure while handling failure event: {}", e1.getMessage(), e1);
}
}
}
}