apache-activemq-5.14.0学习总结
一、什么是ActiveMQ?
百度百科对ActiveMQ的描述:
二、ActiveMQ有什么优点?哪些情况下适合使用ActiveMQ?
2.1ActiveMQ 优点:
(1) 跨平台(JAVA编写与平台无关有,ActiveMQ几乎可以运行在任何的JVM上)
(2) 支持多种语言
(3) 降低系统间模块的耦合度,解耦(消息的发送方和接收方并不需要彼此联系,也不需要受对方的影响,即解耦和)
(4) 对Spring的,软件扩展性好
(5) 自动重连功能
2.2 ActiveMQ的使用场景:
多个项目之间集成
系统前后端隔离,屏蔽高安全区(处理高并发)
再举两个例子:(来自知乎上的观点);
例一:
假设用户在你的软件中注册,服务端收到用户的注册请求后,它会做这些操作:
校验用户名等信息,如果没问题会在数据库中添加一个用户记录
如果是用邮箱注册会给你发送一封注册成功的邮件,手机注册则会发送一条短信
分析用户的个人信息,以便将来向他推荐一些志同道合的人,或向那些人推荐他
发送给用户一个包含操作指南的系统通知 等等……
但是对于用户来说,注册功能实际只需要第一步,只要服务端将他的账户信息存到数据库中他便可以登录上去做他想做的事情了。至于其他的事情,非要在这一次请求中全 部完成么?值得用户浪费时间等你处理这些对他来说无关紧要的事情么?所以实际当第一步做完后,服务端就可以把其他的操作放入对应的消息队列中然后马上返回用户结 果,由消息队列异步的进行这些操作。
或者还有一种情况,同时有大量用户注册你的软件,再高并发情况下注册请求开始出现一些问题,例如邮件接口承受不住,或是分析信息时的大量计算使cpu满载,这将会出 现虽然用户数据记录很快的添加到数据库中了,但是却卡在发邮件或分析信息时的情况,导致请求的响应时间大幅增长,甚至出现超时,这就有点不划算了。面对这种情况一 般也是将这些操作放入消息队列(生产者消费者模型),消息队列慢慢的进行处理,同时可以很快的完成注册请求,不会影响用户使用其他功能。
所以在软件的正常功能开发中,并不需要去刻意的寻找消息队列的使用场景,而是当出现性能瓶颈时,去查看业务逻辑是否存在可以异步处理的耗时操作,如果存在的话便可 以引入消息队列来解决。否则盲目的使用消息队列可能会增加维护和开发的成本却无法得到可观的性能提升,那就得不偿失了。
例二:
跨系统的异步通信,所有需要异步交互的地方都可以使用消息队列。就像我们除了打电话(同步)以外,还需要发短信,发电子邮件(异步)的通讯方式。
多个应用之间的耦合,由于消息是平台无关和语言无关的,而且语义上也不再是函数调用,因此更适合作为多个应用之间的松耦合的接口。基于消息队列的耦合,不需要发送 方和接收方同时在线。
在企业应用集成(EAI)中,文件传输,共享数据库,消息队列,远程过程调用都可以作为集成的方法。
应用内的同步变异步,比如订单处理,就可以由前端应用将订单信息放到队列,后端应用从队列里依次获得消息处理,高峰时的大量订单可以积压在队列里慢慢处理掉。由于 同步通常意味着阻塞,而大量线程的阻塞会降低计算机的性能。
消息驱动的架构(EDA),系统分解为消息队列,和消息制造者和消息消费者,一个处理流程可以根据需要拆成多个阶段(Stage),阶段之间用队列连接起来,前一个阶段 处理的结果放入队列,后一个阶段从队列中获取消息继续处理。
应用需要更灵活的耦合方式,如发布订阅,比如可以指定路由规则。
三、ActiveMQ官方(最新)下载地址 http://activemq.apache.org/
提示 Kafka 这个也是消息中间件也是Apche的这里不做研究
四、ActiveMQ官方(最新)下载地址
4.1 与Tomcat相似解压即可, 解压后
bin:其中包含MQ的启动脚本
conf:包含MQ的所有配置文件
data:日志文件及持久性消息数据
example:MQ的示例
lib:MQ运行所需的所有Lib
webapps:MQ的Web控制台及一些相关的DEMO
activemq-all-5.5.0.jar:所有MQ JAR包的集合,用于用户系统调用
4.2 启动ActiveMQ, 进入bin后xp系统双击activemq.bat 如果win7系统是32或64位启动对应的activemq.bat批处理文件
ActiveMQ四种协议的简单介绍:
TCP协议:默认端口61616
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI 模型中,它完成第四层传输层所指定的功能,用户数据报协议(UDP)是同一层内[1] 另一个重要的传输协议。在因特网协议族(Internet protocol suite)中,TCP层 是位于IP层之上,应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。
AMQP协议:默认端口5672
即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议 的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制。
STOMP协议:默认端口61613
STOMP,Streaming Text Orientated Message Protocol,是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单 文本协议
MQTT协议:默认端口1883
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几 乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器(比如通过Twitter让房屋联网)的通信协议。
OpenWire协议:(😀控制台上没有看到)官方网站对其的介绍如下:
OpenWire
is our cross language Wire Protocol to allow native access to ActiveMQ
from a number of different languages and platforms. The Java OpenWire
transport is the default transport in ActiveMQ 4.x or later. For other
languages see the following...
ActiveMQ默认使用的TCP连接端口是61616, 通过查看该端口的信息可以测试ActiveMQ是否成功启动 netstat -an|find “61616”
4.3 访问ActiveMQ的WEB管理员监控控制台 http://127.0.0.1:8161/admin/
4.3 停止ActiveMQ服务,按Ctrl+Shift+C,之后输入y即可
注意:启动ActiveMQ时闪退问题解决方案
解压缩apache-activemq-5.14.0-bin.zip后双击/bin目录下的activemq.bat批处理文件发现启动窗口一闪而过无法启动,最后找到原因是因为在环境变量-系统变量中
JAVA_HOME、classpath、Path这些系统变量中没有添加JDK相关的安装路径。
JAVA_HOME下添加"D:\soft\Java\jdk1.7.0_09"
classpath下添加".;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar"
Path下添加"%JAVA_HOME%\bin;"
特别注意如果系统变量中没有classpath变量、JAVA_HOME变量则新增然后再添加上述相关路径。
上述路径是我本地JDK版本安装的路径。如果JDK安装在其他版本下则相应的调整下就可以了。
五、使用TCP方式测试ActiveMQ:
Sender code
package yingyue; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.DeliveryMode; import javax.jms.Destination; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; public class Sender { private static final int SEND_NUMBER = 5; public static void main(String[] args) { // ConnectionFactory :连接工厂,JMS 用它创建连接 ConnectionFactory connectionFactory; // Connection :JMS 客户端到JMS Provider 的连接 Connection connection = null; // Session: 一个发送或接收消息的线程 Session session; // Destination :消息的目的地;消息发送给谁. Destination destination; // MessageProducer:消息发送者 MessageProducer producer; // TextMessage message; // 构造ConnectionFactory实例对象,此处采用ActiveMq的实现jar connectionFactory = new ActiveMQConnectionFactory( ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "tcp://localhost:61616"); try { // 构造从工厂得到连接对象 connection = connectionFactory.createConnection(); // 启动 connection.start(); // 获取操作连接 session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE); // 获取session注意参数值xingbo.xu-queue是一个服务器的queue,须在在ActiveMq的console配置 destination = session.createQueue("TestQueue"); // 得到消息生成者【发送者】 producer = session.createProducer(destination); // 设置不持久化,此处学习,实际根据项目决定 producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); // 构造消息,此处写死,项目就是参数,或者方法获取 sendMessage(session, producer); session.commit(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != connection) connection.close(); } catch (Throwable ignore) { } } } public static void sendMessage(Session session, MessageProducer producer) throws Exception { for (int i = 1; i <= SEND_NUMBER; i++) { TextMessage message = session .createTextMessage("ActiveMq " + "HelloWorld " + i); // 发送消息到目的地方 System.out.println("发送消息:" + "ActiveMq " + "HelloWorld " + i); producer.send(message); } } }
Receiver code
package yingyue; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; import javax.jms.MessageConsumer; import javax.jms.Session; import javax.jms.TextMessage; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQConnectionFactory; public class Receiver { public static void main(String[] args) { // ConnectionFactory :连接工厂,JMS 用它创建连接 ConnectionFactory connectionFactory; // Connection :JMS 客户端到JMS Provider 的连接 Connection connection = null; // Session: 一个发送或接收消息的线程 Session session; // Destination :消息的目的地;消息发送给谁. Destination destination; // 消费者,消息接收者 MessageConsumer consumer; connectionFactory = new ActiveMQConnectionFactory( ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, "tcp://localhost:61616"); try { // 构造从工厂得到连接对象 connection = connectionFactory.createConnection(); // 启动 connection.start(); // 获取操作连接 session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE); // 获取session注意参数值xingbo.xu-queue是一个服务器的queue,须在在ActiveMq的console配置 destination = session.createQueue("TestQueue"); consumer = session.createConsumer(destination); while (true) { //设置接收者接收消息的时间,为了便于测试,这里谁定为100s TextMessage message = (TextMessage) consumer.receive(100000); if (null != message) { System.out.println("收到消息:" + message.getText()); } else { break; } } } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != connection) connection.close(); } catch (Throwable ignore) { } } } }
如下: