这不,为了考验下自己的学习成果,上班的第一天成小胖就去找架构师老王互换 ActiveMQ 干系的知识,还顺便向老王讨了个红包,可把成小胖给高兴坏了。
“来,根据你的理解说下 ActiveMQ 是什么。”
“这个大略,ActiveMQ 是一个 MOM,详细来说是一个实现了 JMS 规范的系统间远程通信的代理。它……”
“等等,先阐明下什么是 MOM。”
“好。MOM 便是面向中间件(Message-oriented middleware),是用于以分布式运用或系统中的异步、松耦合、可靠、可扩展和安全通信的一类软件。MOM 的总体思想是它作为发送器和吸收器之间的中介,这种中介供应了一个全新水平的松耦合。”
“JMS呢?”
成小胖是个追求极致的人,为理解释得更普通易懂,索性搬来一块白板边画边说。
“JMS 叫做 Java 做事(Java Message Service),是 Java 平台上有关面向 MOM 的技能规范,旨在通过供应标准的产生、发送、吸收和处理的 API 简化企业运用的开拓,类似于 JDBC 和关系型数据库通信办法的抽象。”
“嗯,很好。下面的这些观点你也须要特殊理解下”:
Provider:纯 Java 措辞编写的 JMS 接口实现(比如 ActiveMQ 便是)Domains:通报办法,包括点对点(P2P)、发布/订阅(Pub/Sub)两种Connection factory:客户端利用连接工厂来创建与 JMS provider 的连接Destination:被寻址、发送以及吸收的工具“你来说说这个中 P2P 和 Pub/Sub 的差异吧”,老王给成小胖抛出了一个问题。
成小胖可不是吃素的,毕竟假如吃素的话他也吃不到这么胖……这些基本观点对他来说都是小事一桩:
P2P (点对点)域利用 queue 作为 Destination,可以被同步或异步的发送和吸收,每个只会给一个 Consumer 传送一次。
Consumer 可以利用 MessageConsumer.receive() 同步地吸收,也可以通过利用MessageConsumer.setMessageListener() 注册一个 MessageListener 实现异步吸收。
多个 Consumer 可以注册到同一个 queue 上,但一个只能被一个 Consumer 所吸收,然后由该 Consumer 来确认。并且在这种情形下,Provider 对所有注册的 Consumer 以轮询的办法发送。
Pub/Sub(发布/订阅,Publish/Subscribe)域利用 topic 作为 Destination,发布者向 topic 发送,订阅者注册吸收来自 topic 的。发送到 topic 的任何都将自动通报给所有订阅者。吸收办法(同步和异步)与 P2P 域相同。
除非显式指定,否则 topic 不会为订阅者保留。当然,这可以通过持久化(Durable)订阅来实现的保存。这种情形下,当订阅者与 Provider 断开时,Provider 会为它存储。当持久化订阅者重新连接时,将会受到所有的断连期间未消费的。
“嗯,总结的很不错,上面的这些知识是学习 ActiveMQ 的理论根本,是必须要节制的。”
“既然 JMS 是一个通用的规范,那么利用它创建运用程序肯定也有一个通用的步骤吧?”老王追问道。
“有的有的。要不您来说说这个通用步骤?就当我考考您,哈哈!
”成小胖故作聪明,自以为老王作为架构师不会关注这些太详细的实现细节。
然而老王平日里亲力亲为,至今还常常撸代码,怎么会被这种小 case 所难倒?于是老王分分钟给出答案:
获取连接工厂利用连接工厂创建连接启动连接从连接创建会话获取 Destination创建 Producer,或创建 Producer创建 message创建 Consumer,或发送或吸收message发送或吸收 message创建 Consumer注册监听器(可选)发送或吸收 message关闭资源(connection, session, producer, consumer 等)“66666,厉害啊我的王哥!
”成小胖的小聪明被老王击得粉碎!
“你嘴皮子耍够了吧,还是多动动手吧。现在你手写上面步骤对应的代码实现吧”,老王给了成小胖一个眼神,让他自己逐步体会……
成小胖也不是省油的灯,立时擦干净白板,现场撸了起来(是撸代码,撸代码,撸代码,主要的事情说三遍):
public class JMSDemo { ConnectionFactory connectionFactory; Connection connection; Session session; Destination destination; MessageProducer producer; MessageConsumer consumer; Message message; boolean useTransaction = false; try { Context ctx = new InitialContext(); connectionFactory = (ConnectionFactory) ctx.lookup(\公众ConnectionFactoryName\公众); //利用ActiveMQ时:connectionFactory = new ActiveMQConnectionFactory(user, password, getOptimizeBrokerUrl(broker)); connection = connectionFactory.createConnection(); connection.start(); session = connection.createSession(useTransaction, Session.AUTO_ACKNOWLEDGE); destination = session.createQueue(\公众TEST.QUEUE\"大众); //生产者发送 producer = session.createProducer(destination); message = session.createTextMessage(\"大众this is a test\公众); //消费者同步吸收 consumer = session.createConsumer(destination); message = (TextMessage) consumer.receive(1000); System.out.println(\公众Received message: \公众 + message); //消费者异步吸收 consumer.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { if (message != null) { doMessageEvent(message); } } }); } catch (JMSException e) { ... } finally { producer.close(); session.close(); connection.close(); }}
老王满意的点点头:“还算不赖哈~ JMS 通用的规范咱们都聊完了,下面就来聊点 ActiveMQ 更详细点的东西咯。”
“好啊好啊。要不我先基于自己的学习讲讲 ActiveMQ 的存储,您看看我哪里讲的不对或者遗漏的,可好?”成小胖发挥了他一向的积极主动的作风,当然内心里还是想得到老王的赞许。
“行,那就开始吧。”
ActiveMQ 在 queue 中存储 Message 时,采取前辈先出顺序(FIFO)存储。同一韶光一个被分派给单个消费者,且只有当 Message 被消费并确认时,它才能从存储中删除。
对付持久化订阅者来说,每个消费者得到 Message 的副本。为了节省存储空间,Provider 仅存储的一个副本。持久化订阅者掩护了指向下一个 Message 的指针,并将其副本分派给消费者。以这种办法实现存储,由于每个持久化订阅者可能以不同的速率消费 Message,或者它们可能不是全部同时运行。此外,因每个 Message 可能存在多个消费者,以是在它被成功地通报给所有持久化订阅者之前,不能从存储中删除。
“很好,上面这段知识非常主要。实在我们可以通过表格来更清晰地展示”,老王补充道,并在白板上画了以下表格:
成小胖虽然对以上特性做过实践比拟,但是并没有想到去画一个表格出来使比拟更加清晰易懂。特殊是当他看到老王随时就画出这个表格时便惊叹不已,大声喊道:“老王你太牛了,真是爱去世你了!
”
周围的同事听到后,都齐刷刷的往这边看过来。
此情此景,老王也不好意思了:“诶诶诶,说话把稳哈,不要让人以为我们在搞基。回归正题,你再说说 ActiveMQ 常用的存储办法吧。”
成小胖羞涩的点点头,迅速地回归原态,一五一十地提及来。
1.KahaDB
ActiveMQ 5.3 版本起的默认存储办法。KahaDB存储是一个基于文件的快速存储,设计目标是易于利用且尽可能快。它利用基于文件的数据库意味着没有第三方数据库的先决条件。
要启用 KahaDB 存储,须要在 activemq.xml 中进行以下配置:
<broker brokerName=\"大众broker\"大众 persistent=\"大众true\"大众 useShutdownHook=\"大众false\"大众> <persistenceAdapter> <kahaDB directory=\公众${activemq.data}/kahadb\"大众 journalMaxFileLength=\"大众16mb\"大众/> </persistenceAdapter></broker>
2.AMQ
与 KahaDB 存储一样,AMQ存储利用户能够快速启动和运行,由于它不依赖于第三方数据库。AMQ 存储库是可靠持久性和高性能索引的事务日志组合,当吞吐量是运用程序的紧张需求时,该存储是最佳选择。但由于它为每个索引利用两个分开的文件,并且每个 Destination 都有一个索引,以是当你打算在代理中利用数千个行列步队的时候,不应该利用它。
<persistenceAdapter> <amqPersistenceAdapter directory=\"大众${activemq.data}/kahadb\公众 syncOnWrite=\"大众true\"大众 indexPageSize=\"大众16kb\公众 indexMaxBinSize=\公众100\"大众 maxFileLength=\公众10mb\公众 /></persistenceAdapter>
3.JDBC
选择关系型数据库,常日的缘故原由是企业已经具备了管理关系型数据的专长,但是它在性能上绝对不优于上述存储实现。事实是,许多企业利用关系数据库作为存储,是由于他们更乐意充分利用这些数据库资源。
<beans> <broker brokerName=\公众test-broker\"大众 persistent=\"大众true\公众 xmlns=\"大众http://activemq.apache.org/schema/core\"大众> <persistenceAdapter> <jdbcPersistenceAdapter dataSource=\"大众#mysql-ds\公众/> </persistenceAdapter> </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://localhost/activemq?relaxAutoCommit=true\"大众/> <property name=\"大众username\"大众 value=\"大众activemq\"大众/> <property name=\公众password\"大众 value=\公众activemq\"大众/> <property name=\公众maxActive\"大众 value=\"大众200\公众/> <property name=\"大众poolPreparedStatements\"大众 value=\"大众true\"大众/> </bean></beans>
4.内存存储
内存存储器将所有持久保存在内存中。在仅存储有限数量 Message 的情形下,内存存储会很有用,由于 Message 常日会被快速花费。在 activema.xml 中将 broker 元素上的 persistent 属性设置为 false 即可。
<broker brokerName=\"大众test-broker\"大众 persistent=\"大众false\"大众 xmlns=\公众http://activemq.apache.org/schema/core\"大众> <transportConnectors> <transportConnector uri=\"大众tcp://localhost:61635\"大众/> </transportConnectors></broker>
老王听完后露出赞许的笑颜:“连配置都能写的这么详细,看来你确实是做了不少作业,给你点个赞。”老王究竟不会吝啬自己的赞颂,他也明白这些赞颂对成小胖意味着什么。
成小胖得到了老王的讴歌,心里也是吃了蜜一样平常。
没等成小胖说话,老王拿起笔走到白板前,说:“下面就根据我在事情中的经历,给你讲讲 ActiveMQ 的支配模式。”
1.单例模式
这个就不啰嗦了,略过。
2.无共享主从模式
这是最大略的 Provider 高可用性的方案,主从节点分别存储 Message。从节点须要配置为连接到主节点,并且须要分外配置其状态。
所有命令(,确认,订阅,事务等)都从主节点复制到从节点,这种复制发生在主节点对其吸收的任何命令生效之前。并且,当主节点收到持久,会等待从节点完成的处理(常日是持久化到存储),然后再自己完成的处理(如持久化到存储)后,再返回对 Producer 的回执。
从节点不启动任何传输,也不能接管任何客户端或网络连接,除非主节点失落效。当主节点失落效后,从节点自动成为主节点,并且开启传输并接管连接。这是,利用 failover 传输的客户端就会连接到该新主节点。
Broker 连接配置如下:
failover://(tcp://masterhost:61616,tcp://slavehost:61616)?randomize=false
但是,这种支配模式有一些限定,
主节点只会在从节点连接到主节点时复制其活动状态,因此当从节点没有连接上主节点之前,任何主节点处理的 Message 或者确认都会在主节点失落效后丢失。不过你可以通过在主节点设置 waitForSlave 来避免,这样就逼迫主节点在没有任何一个从节点连接上的情形下接管连接。便是主节点只能有一个从节点,并且从节点不许可再有其他从节点。把正在运行的单例配置成无共享主从,或者配置新的从节点时,你都要停滞当前做事,修正配置后再重启才能生效。在可以接管一些故障停机韶光的情形下,可以利用该模式。
从节点配置:
<services> <masterConnector remoteURI=\"大众tcp://remotehost:62001\公众 userName=\"大众Rob\公众 password=\"大众Davies\公众/></services>
此外,可以配置 shutdownOnMasterFailure 项,表示主节点失落效后安全关闭,担保没有丢失,许可管理员掩护一个新的从节点。
3.共享存储主从模式
许可多个代理共享存储,但任意时候只有一个是活动的。这种情形下,当主节点失落效时,无需人工干预来掩护运用的完全性。其余一个好处便是没有从节点数的限定。
有两种细分模式:
(1)基于数据库
它会获取一个表上的排它锁,以确保没有其他 ActiveMQ 代理可以同时访问数据库。其他未得到锁的代理则处于轮询状态,就会被当做是从节点,不会开启传输也不会接管连接。
(2)基于文件系统
须要获取分布式共享文件锁,linux 系统下推举用 GFS2。
看到这些干货,成小胖欣喜若狂一边听一边记,等老王讲完后他还没记完。而老王则趁机喝了杯铁不雅观音润润嗓子。
在记录完老王所讲的支配模式后,成小胖也不好意思再让老王连续讲下去了,毕竟他知道老王常年加班腰间盘突出,不能永劫光站着。
“王哥您坐着安歇下,我再给您讲讲我所理解的 ActiveMQ 的网络连接,中不中?”
“中。没事儿,我身体好着呢~”老王知道成小胖担心他的腰,但他还是那个倔脾气。成小胖也不敢多延误韶光,立马开讲。
1.代理网络
支持将 ActiveMQ 代理链接到不同拓扑,这便是被人们熟知的代理网络。
ActiveMQ 网络利用存储和转发的观点,个中总是存储在本地代理中,然后通过网络转发到另一个代理。
当连接建立后,远程代理将把包含其所有持久和活动消费者目的地的信息通报给本地代理,本地代理根据信息决定远程代理感兴趣的 Message 并将它发送给远程代理。
如果希望网络是双向的,您可以利用网络连接器将远程代理配置为指向本地代理,或将网络连接器配置为双工,以便双向发送。
<networkConnectors> <networkConnector uri=\"大众static://(tcp://backoffice:61617)\"大众 name=\"大众bridge\"大众 duplex=\公众true\公众 conduitSubscriptions=\"大众true\公众 decreaseNetworkConsumerPriority=\公众false\"大众> </networkConnector></networkConnectors>
把稳,配置的顺序很主要:
网络连接——须要在存储前建立好连接,对应 networkConnectors 元素存储——须要在传输前配置好,对应 persistenceAdapter 元素传输——末了配置,对应 transportConnectors 元素2.网络创造
(1)动态创造
利用多播来支持网络动态创造。配置如下:
<networkConnectors> <networkConnector uri=\"大众multicast://default\公众/></networkConnectors>
个中,multicast:// 中的默认名称表示该代理所属的组。因此利用此办法时,强烈推举你利用一个独特的组名,避免你的代理连接到其他不干系代理。
(2)静态创造
静态创造接管代理 URI 列表,并将考试测验按列表中确定的顺序连接到远程代理。
<networkConnectors> <networkConnector uri=\"大众static:(tcp://remote-master:61617,tcp://remote-slave:61617)\"大众/></networkConnectors>
干系配置如下:
initialReconnectDelay:默认值1000,表示考试测验连接前的时延。maxReconnectDelay:默认值30000,表示连接失落败后到重新建立连接之间的时延,仅在useExponentialBackOff 启用时生效。useExponentialBackOff:默认值 true,如果启用,表示每次失落败后增加重修连接的时延。backOffMultiplier:默认值2,表示启用 useExponentialBackOff 后每次的时延增量须要把稳的是,网络连接将始终考试测验建立到远程代理的连接。须要把稳的是,网络连接将始终考试测验建立到远程代理的连接。
(3)多连接场景
当网络负载高时,利用多连接很故意义。但是你须要确保不会重复通报,这可以通过过滤器来实现。
<networkConnectors> <networkConnector uri=\公众static://(tcp://remotehost:61617)\"大众 name=\公众queues_only\公众 duplex=\"大众true\"大众 <excludedDestinations> <topic physicalName=\公众>\"大众/> </excludedDestinations> </networkConnector> <networkConnector uri=\"大众static://(tcp://remotehost:61617)\"大众 name=\"大众topics_only\"大众 duplex=\"大众true\"大众 <excludedDestinations> <queue physicalName=\"大众>\"大众/> </excludedDestinations> </networkConnector></networkConnectors>
讲完后成小胖如释重负,由于上面这些知识点虽然看起来很少,但他却花了很多韶光看了很多英文资料,同时反复实践才理解透的呢。
在成小胖讲的这段韶光,老王一贯坐在转椅上,这会儿他的腰也舒畅了很多。老王站起来拍了拍成小胖的肩膀:“这个知识点虽然理解起来有点晦涩,但是你阐明得还是挺不错的。通过本日的互换,可以看出你对 ActiveMQ 的根本知识有了不错的节制,今后呢还是要多加深入实践,这样才能利用它供应高质量的做事。”
老王的手机溘然响了,是要去开会了:“本日就到这儿吧,我有个会议要参加。”
“好,感激王哥的耐心辅导。希望你多把稳身体。”
老王鬼魅的一笑:“嗯,放心吧,隔壁有对年轻人刚结婚,听Ta们说最近想要个小baby,以是我肯定会保养好自己的身体的。”
成小胖:“……”
“哈哈,开玩笑的!
”
“……”
作者:cyfonly原文地址:cnblogs.com/cyfonly/p/6380860.html