ActiveMQ介绍、安装与测试
ActiveMQ介绍与安装
MQ是消息中间件,是一种在分布式系统中应用程序借以传递消息的媒介,常用的有Kafka、RabbitMQ、RocketMQ、ActiveMQ。
我们要介绍的ActiveMQ是Apache下的开源项目,可以说学完ActiveMQ后,我们可以横向学习其他几个中间件也会变得容易。
下载activemq的压缩包上传到Linux服务器,
通过tar命令解压后,
可以看到如下目录:
bin 目录下存放了activemq的启动脚本。如图中的activemq
conf 目录下面存放一些配置文件,如配置用户名、密码、端口等信息。
docs 目录下存放了开发文档。
data 目录下是ActiveMQ进行消息持久化存放的地方,默认采用的是kahadb,当然我们可以采用leveldb,或者采用JDBC存储到MySQL。
activemq的端口号: 61616
相关命令:
启动: ./activemq start
带日志启动: ./activemq start > /myactiveMQ/run_activemq.log
重启: ./activemq restart
关闭: ./activemq stop
查看状态: ps -ef | grep activemq | grep -v grep
netstat -anp | grep 61616
lsof -i:61616
如若想让其他服务器访问还需如下配置:
需修改 conf/jetty.xml文件:
jettyPort的Ip地址为 0.0.0.0
设置可通过防火墙的端口:
firewall-cmd --zone=public --add-port=61616/tcp --permanent
firewall-cmd --zone=public --add-port=8161/tcp --permanent
防火墙开放端口:
firewall-cmd --list-ports
重启防火墙:
firewall-cmd --reload
接下来打开Apache ActiveMQ控制台:
http://服务器ip地址:8161/admin
默认用户名:admin
默认密码: admin
备注:
采用61616端口提供JMS服务
采用8161端口提供管理控制台服务
如下图所示,说明已经安装成功了。
在什么场景下使用消息中间件?
为什么要使用消息中间件呢?我们举个例子。
假如学生需要向老师提问问题,老师来为学生解答问题,老师每次只能为一位学生解答,那么后面的学生只能等待。如下图所示
如果我们的系统也是这样的,当大量的任务需要处理时,就会造成阻塞,严重影响效率。
现在我们在这个例子中再加入一个角色班长(MQ),让需要提问题的学生直接去找班长,班长负责统计学生的问题,学生姓名等信息。这样每一个学生提问题被班长统计后就可以直接离开,无需等待。
如下图所示
这样我们的执行效率就提高了很多。
消息中间件能干嘛
从上面的例子可以看出:
- 解耦:解决了耦合调用的问题。将学生提问与老师解答问题分开。
- 异步: 异步模型。学生提问无需等待老师回答而陷入阻塞,提问完直接离开。
- 削峰:抵御洪峰流量,达到保护主业务的目的。中间件提供了一个缓冲作用。
在设计系统时可以明确达到的目标
- 要做到系统解耦,当新的模块进来时,可以左到代码改动最小,能够解耦
- 设置流量缓冲池,可以让后端系统按照自身吞吐能力进行消费,不被冲跨,能够削峰。
- 强弱依赖梳理能够将非关键调用链路的操作异步化并提升整体系统的吞吐能力,能够异步。
在点对点的消息传递域中,目的地被称为队列(queue)
在发布订阅消息传递域中,目的地被称为主题(topic)
ActiveMQ测试
JMS编码总体架构
- 创建一个maven工程,导入如下jar包:
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.15.9</version>
</dependency>
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>3.4</version>
</dependency>
- 消息生产者测试代码:
import javax.jms.Connection;
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 ZXG
*/
public class JmsProduce {
public static final String ACTIVEMQ_URL = "tcp://129.*.*.143:61616"; //服务器地址
public static final String QUEUE_NAME = "queue01"; //队列名
public static void main(String[] args) throws JMSException {
// 1 创建连接工厂,按照给定的url地址,采用默认用户名和密码
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 2 通过连接工程,获得连接Connection
Connection connection = activeMQConnectionFactory.createConnection();
// 启动
connection.start();
// 3 创建会话Session
// 参数:事务/签收
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 4 创建目的地(具体是队列queue 还是主题topic)
Queue queue = session.createQueue(QUEUE_NAME);
//5 创建消息的生产者
MessageProducer messageProducer = session.createProducer(queue);
//6 通过使用messageProducer生产3条消息发送到MQ的消息队列里面
for(int i=0;i<3;i++) {
//7 创建消息,好比学生按照要求写好的消息
TextMessage message = session.createTextMessage("test msg:"+i); //可以理解为一个字符串
//8 通过消息生产者 messageProducer 发布
messageProducer.send(message);
}
//9 关闭资源
//后创建的先关闭
messageProducer.close();
session.close();
connection.close();
System.out.println("消息发布完成");
}
}
运行后再看控制台我们已经可以看到我们创建的队列了,还有待处理的邮件数,消费者人数,入队数量与出队数量、运作方式等信息。
再次查看queue01的具体消息。
查看我们的消息内容:
3. 消息消费者测试代码
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
* 消息消费者
* @author ZXG
*/
public class JmsConsumer {
public static final String ACTIVEMQ_URL = "tcp://129.*.*.143:61616";
public static final String QUEUE_NAME = "queue01";
public static void main(String[] args) throws JMSException {
// 1 创建连接工厂,按照给定的url地址,采用默认用户名和密码
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
// 2 通过连接工程,获得连接Connection
Connection connection = activeMQConnectionFactory.createConnection();
// 启动
connection.start();
// 3 创建会话Session
// 参数:事务/签收
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 4 创建目的地(具体是队列queue 还是主题topic)
Queue queue = session.createQueue(QUEUE_NAME);
//5 创建消费者
MessageConsumer messageConsumer = session.createConsumer(queue);
while(true) {
//6 获取消息
TextMessage textMessage = (TextMessage) messageConsumer.receive(); //一直等
//messageConsumer.receive(timeout); //设定等待时间
if(null==textMessage) {
break;
}
System.out.println("消费者接收到的消息:"+textMessage.getText());
}
//7 关闭资源
messageConsumer.close();
session.close();
connection.close();
}
}
先查看控制台运行结果:
这刚好是我们消息生产者发布的消息。
此时我们的程序还没有运行结束,因为messageConsumer.receive(); 会一直等。
发现消费者数量为1,手动关闭程序,此时消费者数量已经变成0。也可以给该方法设置参数,messageConsumer.receive(4000L);在等待设定时间后自动返回。