ActiveMQ的网络连接分为静态连接和动态连接。本章研究静态连接。
1.ActiveMQ的networkConnector是什么
在某些情况下,需要多个ActiveMQ的Broker做集群,那么就涉及到Broker到Broker的通信,这个就称为ActiveMQ的networkConnector。也就是broker-to-broker
ActiveMQ的networkConnector默认是单向的,一个Broker在一端发送消息,另一个Broker在另一端接收消息,这就是所谓的"桥接"。
ActiveMQ也支持双向链接,创建一个双向的通道对于两个Broker不仅发送消息而且也能从相同的通道接收消息,通常作为duplex connector来映射。
2.有两种配置Client到Broker的链接方式
第一种: Client通过Staticlly配置的方式去连接Broker(静态链接)
第二种: Client通过discover agent来dynamically的发现Brokers(动态链接)===后面学习
3.Static networks:
(在前面集群环境的搭建过程实际就是静态连接:https://www.cnblogs.com/qlqwjy/p/9728425.html)
Static networkConnector是用于创建一个静态的配置对于网络中的多个Broker,这种协议用于复合url,一个复合url包括多个url地址,格式如下:
static:(uri1,uri2,uri3, ...)?key=value
3.1搭建两个broker
关于创建多个broker的方式参考:https://www.cnblogs.com/qlqwjy/p/10463660.html
这里创建两个broker,端口分别是:tcp://0.0.0.0:61616和tcp://0.0.0.0:61617,对应的jetty的http端口是8161和8162。
现在在61616的那个activemq.xml中增加静态连接:
<transportConnectors> <!-- DOS protection, limit concurrent connections to 1000 and frame size to 100MB --> <transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/> </transportConnectors> <networkConnectors> <networkConnector name="local_network" uri="static:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617)"></networkConnector> </networkConnectors>
接下来正常启动两个broker,并且从http验证正常启动。
3.2测试:向61616发送消息,从61617接收消息
package cn.qlq.activemq; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; import javax.jms.TextMessage; import org.apache.activemq.ActiveMQConnectionFactory; /** * 生产消息 * * @author QiaoLiQiang * @time 2018年9月18日下午11:04:41 */ public class MsgProducer { // 默认端口61616 private static final String url = "tcp://localhost:61616/"; private static final String queueName = "myQueue"; private static Session session = null; public static void main(String[] args) throws JMSException { // 1创建ConnectionFactory ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url); // 2.由connectionFactory创建connection Connection connection = connectionFactory.createConnection(); // 3.启动connection connection.start(); // 4.创建Session===第一个参数是是否事务管理,第二个参数是应答模式 session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); // 5.创建Destination(Queue继承Queue) Queue destination = session.createQueue(queueName); // 6.创建生产者producer MessageProducer producer = session.createProducer(destination); for (int i = 0; i < 10; i++) { // 7.创建Message,有好多类型,这里用最简单的TextMessage TextMessage tms = session.createTextMessage("textMessage:" + i); // 设置附加属性 tms.setStringProperty("str", "stringProperties" + i); // 8.生产者发送消息 producer.send(tms); } // 9.提交事务 session.commit(); // 10.关闭connection session.close(); connection.close(); } }
package cn.qlq.activemq; import java.util.Enumeration; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageListener; import javax.jms.Queue; import javax.jms.Session; import javax.jms.TextMessage; import org.apache.activemq.ActiveMQConnectionFactory; /** * 消费消息 * * @author QiaoLiQiang * @time 2018年9月18日下午11:26:41 */ public class MsgConsumer { // 默认端口61616 private static final String url = "tcp://localhost:61617/"; private static final String queueName = "myQueue"; public static void main(String[] args) throws JMSException { // 1创建ConnectionFactory ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url); // 2.由connectionFactory创建connection Connection connection = connectionFactory.createConnection(); Enumeration jmsxPropertyNames = connection.getMetaData().getJMSXPropertyNames(); while (jmsxPropertyNames.hasMoreElements()) { String nextElement = (String) jmsxPropertyNames.nextElement(); System.out.println("JMSX name ===" + nextElement); } // 3.启动connection connection.start(); // 4.创建Session===第一个参数是是否事务管理,第二个参数是应答模式 Session session = connection.createSession(true, Session.SESSION_TRANSACTED); // 5.创建Destination(Queue继承Queue) Queue destination = session.createQueue(queueName); // 6.创建消费者consumer MessageConsumer consumer = session.createConsumer(destination); int i = 0; while (i < 5) { TextMessage textMessage = (TextMessage) consumer.receive(); System.out.println("接收消息:" + textMessage.getText() + ";属性" + textMessage.getStringProperty("str")); i++; if (i == 5) {// 确保消费完所有的消息再进行确认 textMessage.acknowledge(); } } // 提交事务,进行确认收到消息 session.commit(); session.close(); connection.close(); } }
结果:
总结:
可以看做61617是61616的一个消费者,也就是61616上的消息也可以从61617进行消费,但是61617上生产的消息不能从61616上消费。因为只在61616配置了静态连接,61616可以通过network连接到61616和61617,也就是61617可以作为61616的消费者。
如果想实现在双方都可以生产并消费在61617的activemq.xml配置相同的静态连接就可以了。或者参考下面的第11条配置设置为双向通信。
如果想实现双方的消息同步,那么实现数据共享就可以了,也就是kahaDB的目录共享就可以实现共享数据。
3.3 networkConnector配置的可用属性
1.name: 默认为bridge
2.dynamicOnly: 默认是false,如果为true,持久订阅被激活时才创建对应的网络持久订阅。默认是启动时激活
3.decreaseNetworkConsumerPriority: 默认是false。设定消费者优先权,如果为true,网络的消费者优先级降低为-5。如果为false,则默认跟本地消费者一样为0。(其实这个进一步的理解就是消费者同时从61617和 61616消费消息,通常情况下61617的先获取到消息,也就是静态连接的另一方发而可以获得更多的消息)
4.networkTTL: 默认是1,网络中用于消息和订阅消费的broker数量
5.messageTTL: 默认是1,网络中用于消息的broker数量
6.consumerTTL: 默认是1,网络中用于消费的broker数量
7.conduitSubscriptions: 默认true,是否把同一个broker的多个consumer当做一个来处理(在做集群的时候如果有多个consumer,需要设置为false)
8.dynamicallyIncludedDestinations:默认为空,要包括的动态消息地址,类适于excludedDestinations,如:类似于一个消息的过滤,只向61617传输满足条件的消息===这个用在动态连接
<dynamicallyIncludedDestinations> <queue physicalName="include.test.foo"/> <topic physicalName="include.test.bar"/> </dynamicallyIncludedDestinations>
9.staticallyIncludedDestinations:默认为空,要包括的静态消息地址。类似于excludedDestinations,如:类似于一个消息的过滤,只向61617传输满足条件的消息===这个用在静态连接
<staticallyIncludedDestinations> <queue physicalName="always.include.queue"/> </staticallyIncludedDestinations>
10.excludedDestinations: 默认为空,指定排除的地址,示例如下:(这个是排除的消息)
<networkConnectors> <networkConnector uri="static://(tcp://localhost:61617)" name="bridge" dynamicOnly="false" conduitSubscriptions="true" decreaseNetworkConsumerPriority="false"> <excludedDestinations> <queue physicalName="exclude.test.foo"> <topic physicalName="exclude.test.bar"> </excludedDestinations> <dynamicallyIncludedDestinations> <queue physicalName="include.test.foo"/> <topic physicalName="include.test.bar"/> </dynamicallyIncludedDestinations> <staticallyIncludedDestinations> <queue physicalName="always.include.queue"/> </staticallyIncludedDestinations> </networkConnector> </networkConnectors>
11.duplex:默认是false,设置是否能双向通信。也就是双方都可以作为对方的消费者。
<networkConnectors> <networkConnector duplex="true" name="local_network" uri="static:(tcp://127.0.0.1:61616,tcp://127.0.0.1:61617)"></networkConnector> </networkConnectors>
12. prefetchSize: 默认是1000,持有的未确认的最大消息数量,必须大于0,因为网络消费者不能自己轮询消息
13. suppressDuplicateQueueSubscriptions: 默认false,如果为true,重复的订阅关系一产生即被阻止
14. bridgeTempDestinations: 默认true,是否广播advisory messages来创建临时的destination
15. alwaysSyncSend: 默认false,如果为true,非持久化消息也将使用request/reply方式代替oneway方式发送到远程broker
16. staticBridge: 默认false,如果为true,只有staticallyIncludedDestinations中配置的destination可以被处理,这个参数配置是否是静态桥