消息中间件——ActiveMQ

ActiveMQ

ActiveMQ简介

ActiveMQ 是 Apache 出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个
完全支持 JMS1.1 和 J2EE 1.4 规范的 JMS Provider 实现,尽管 JMS 规范出台已经是很久
的事情了,但是 JMS 在当今的 J2EE 应用中间仍然扮演着特殊的地位。

什么是消息

“消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;
也可以更复杂,可能包含嵌入对象。

什么是队列

在这里插入图片描述

什么是消息队列

“消息队列”是在消息的传输过程中保存消息的容器

常用消息服务应用

  • ActiveMQ
    ActiveMQ 是 Apache 出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持 JMS1.1 和 J2EE 1.4 规范的 JMS Provider 实现。
  • RabbitMQ
    RabbitMQ 是一个在 AMQP 基础上完成的,可复用的企业消息系统。他遵循 Mozilla Public License 开源协议。开发语言为 Erlang
  • RocketMQ
    由阿里巴巴定义开发的一套消息队列应用服务

消息服务的应用场景

消息队列的主要特点是异步处理,主要目的是减少请求响应时间和解耦。所以主要的使用场景就是将比较耗时而且不需要即时(同步)返回结果的操作作为消息放入消息队列。同时由于使用了消息队列,只要保证消息格式不变,消息的发送方和接收方并不需要彼此联系,也不需要受对方的影响,即解耦和
在这里插入图片描述

异步处理

用户注册

用户注册流程:

  • 注册处理以及写数据库
  • 发送注册成功的手机短信
  • 发送注册成功的邮件信息

如果用消息中间件:则可以创建两个线程来做这些事情,直接发送消息给消息中间件,
然后让邮件服务和短信服务自己去消息中间件里面去取消息,然后取到消息后再自己做对应的业务操作。就是这么方便

应用的解耦

订单处理

生成订单流程:

  1. 在购物车中点击结算
  2. 完成支付
  3. 创建订单
  4. 调用库存系统

订单完成后,订单系统并不去直接调用库存系统,而是发送消息到消息中间件,写入一
个订单信息。库存系统自己去消息中间件上去获取,然后做发货处理,并更新库存,这样能
够实现互联网型应用追求的快这一个属性。而库存系统读取订单后库存应用这个操作也是非
常快的,所以有消息中间件对解耦来说也是一个不错的方向。

流量的削峰

秒杀功能

秒杀流程:

  1. 用户点击秒杀
  2. 发送请求到秒杀应用
  3. 在请求秒杀应用之前将请求放入到消息队列
  4. 秒杀应用从消息队列中获取请求并处理。

比如,系统举行秒杀活动,热门商品。流量蜂拥而至 100 件商品,10 万人挤进来怎么
办?10 万秒杀的操作,放入消息队列。秒杀应用处理消息队列中的 10 万个请求中的前 100
个,其他的打回,通知失败。流量峰值控制在消息队列处,秒杀应用不会瞬间被怼死.

JMS

JMS(Java Messaging Service)是 Java 平台上有关面向消息中间件的技术规范,它便于
消息系统中的 Java 应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接
口,简化企业应用的开发。

点对点模型(Point To Point)

生产者发送一条消息到 queue,只有一个消费者能收到。
在这里插入图片描述

发布订阅模型(Publish/Subscribe)

发布者发送到 topic 的消息,只有订阅了 topic 的订阅者才会收到消息
在这里插入图片描述

ActiveMQ 安装

可在ActiveMQ 官网下载安装包: http://activemq.apache.org

注意:

  • ActiveMQ5.10.x 以上版本必须使用 JDK1.8 才能正常使用。
  • ActiveMQ5.9.x 及以下版本使用 JDK1.7 即可正常使用

解压安装文件

tar -zxf apache-activemq-5.9.0-bin.tar.gz

复制应用至本地目录

cp -r apache-activemq-5.9.0 /usr/local/activemq

启动 ActiveMQ

/usr/local/activemq/bin/activemq start

如果activemq没有执行文件权限,需要通过chmod命令进行添加

测试 ActiveMQ

ps aux | grep activemq

见到下述内容即代表启动成功
在这里插入图片描述

管理界面

使用浏览器访问 ActiveMQ 管理应用, 地址如下:
http://ip:8161/admin/
用户名: admin
密码: admin
在这里插入图片描述

修改访问端口:

vim /usr/local/activemq/conf/jetty.xml

在这里插入图片描述

修改用户名和密码

vim /usr/local/activemq/conf/users.properties

然后将下面的键值对
在这里插入图片描述
改成
自定义用户名:自定义密码
即可

注意:无论是修改端口还是修改用户名密码,都需要将activemq重启后才会生效

重启 ActiveMQ

/usr/local/activemq/bin/activemq restart

关闭 ActiveMQ

/usr/local/activemq/bin/activemq stop

配置文件 activemq.xml

配置文件中,配置的是 ActiveMQ 的核心配置信息. 是提供服务时使用的配置. 可以修改
启动的访问端口. 即 java 编程中访问 ActiveMQ 的访问端口.
默认端口为 61616.
使用协议是: tcp 协议.
修改端口:

vim /usr/local/activemq/conf/activemq.xml

在这里插入图片描述

修改端口后, 保存并重启 ActiveMQ 服务即可.

ActiveMQ 目录介绍

  • bin 存放的是脚本文件
  • conf 存放的是基本配置文件
  • data 存放的是日志文件
  • docs 存放的是说明文档
  • examples 存放的是简单的实例
  • lib 存放的是 activemq 所需 jar 包
  • webapps 用于存放项目的目录

ActiveMQ 术语

Destination

目的地,JMS Provider(消息中间件)负责维护,用于对 Message 进行管理的对象。
MessageProducer 需要指定 Destination 才能发送消息,MessageReceiver 需要指定 Destination才能接收消息。

Producer

消息生成者,负责发送 Message 到目的地。

Consumer | Receiver

消息消费者,负责从目的地中消费【处理|监听|订阅】Message

Message

消息,消息封装一次通信的内容

ActiveMQ 应用

ActiveMQ 常用 API 简介

  • ConnectionFactory:链接工厂, 用于创建链接的工厂类型
  • Connection:链接. 用于建立访问 ActiveMQ 连接的类型, 由链接工厂创建
  • Session:会话, 一次持久有效有状态的访问. 由链接创建
  • Destination & Queue:
    目的地, 用于描述本次访问 ActiveMQ 的消息访问目的地. 即 ActiveMQ 服务中的具体队列. 由会话创建.
    interface Queue extends Destination·
  • MessageProducer:
    消息消费者【消息订阅者,消息处理者】, 在一次有效会话中, 用于从 ActiveMQ 服务中获取消息的工具. 由会话创建.
  • Message:
    消息, 通过消息生成者向 ActiveMQ 服务发送消息时使用的数据载体对象或消息消费者从 ActiveMQ 服务中获取消息时使用的数据载体对象. 是所有消息【文本消息,对象消息等】具体类型的顶级接口. 可以通过会话创建或通过会话从 ActiveMQ 服务中获取.

ActiveMQ简单实例

  • pom.xml依赖
<!--https://mvnrepository.com/artifact/org.apache.activemq/activemq-all --> <dependency>
	<groupId>org.apache.activemq</groupId>
	<artifactId>activemq-all</artifactId>
	<version>5.9.0</version>
</dependency>
  • 消息生产者(第一个maven项目)
public class HelloWorldProducer {
	public void sendHelloWorldActiveMQ(String 
msgTest){
		//定义链接工厂
		ConnectionFactory connectionFactory = null;
		//定义链接对象
		Connection connection = null;
		//定义会话
		Session session = null;
		//目的地
		Destination destination = null;
		//定义消息的发送者
		MessageProducer producer = null;
		//定义消息
		Message message = null;
		try{

			/**
			* userName:访问 ActiveMQ 服务的用户名。用户密码。默认的为 admin。
			* password:访问 ActiveMQ 服务的用户名。用户密码。默认的为 admin。
			* brokerURL:访问 ActiveMQ 服务的路径地址。路径结构为:协议名://主机地址:端口号
			*/
			connectionFactory = new ActiveMQConnectionFactory("admin", "admin", "tcp://192.168.70.151:61616");
	
			//创建连接对象
			connection = 
			connectionFactory.createConnection();
			//启动连接
			connection.start();
			/**
			* transacted:是否使用事务 可选值为:true|false
				* true:使用事务 当设置此变量值。Session.SESSION_TRANSACTED
				* false:不适用事务,设置此变量则 acknowledgeMode 参数必须设置
			* acknowledgeMode:
				* Session.AUTO_ACKNOWLEDGE:自动消息确认
				机制
				* Session.CLIENT_ACKNOWLEDGE:客户端确认
				机制
				* Session.DUPS_OK_ACKNOWLEDGE:有副本的客
			户端确认消息机制
			*/
			session = connection.createSession(false, 
	Session.AUTO_ACKNOWLEDGE);
	
			//创建目的地,目的地名称即队列的名称。消息的消费者需要通过此名称访问对应的队列
			destination = session.createQueue("helloworld-destination");
			//创建消息的生产者
			producer = session.createProducer(destination);
			//创建消息对象
			message = session.createTextMessage(msgTest);
			//发送消息
			producer.send(message);
		}catch(Exception e){
			e.printStackTrace();
		}finally{
		//回收消息发送者资源
			if(producer != null){
				try {
					producer.close();
				} catch (JMSException e) {
				// TODO Auto-generated catch block
					e.printStackTrace();
				} 
			}
			
			if(session != null){
				try {
					session.close();
				} catch (JMSException e) {
				// TODO Auto-generated catch block
					e.printStackTrace();
				} 
			}
			
			if(connection != null){
				try {
					connection.close();
				} catch (JMSException e) {
				// TODO Auto-generated catch block
					e.printStackTrace();
				} 
			} 
		} 
	} 
}
  • 消息消费者(第二个maven项目)
public class HelloWorldConsumer {
	public void readHelloWorldActiveMQ() {
		// 定义链接工厂
		ConnectionFactory connectionFactory = null;
		// 定义链接对象
		Connection connection = null;
		// 定义会话
		Session session = null;
		// 目的地
		Destination destination = null;
		// 定义消息的发送者
		MessageConsumer consumer = null;
		// 定义消息
		Message message = null;
		try {
			/** 
			* userName:访问 ActiveMQ 服务的用户名。默认的为 admin。
			* password:访问 ActiveMQ 服务的用户名。默认的为 admin。
			* brokerURL:访问ActiveMQ 服务的路径地址。
			* 路径结构为:协议名://主机地址:端口号
			*/
			connectionFactory = new
			ActiveMQConnectionFactory("admin", "admin","tcp://192.168.70.151:61616");
			// 创建连接对象
			connection =  connectionFactory.createConnection();
			// 启动连接
			connection.start();
			/**
			* transacted:是否使用事务 可选值为:
			* true|false 
				* true:使用事务,当设置此变量值。Session.SESSION_TRANSACTED 
				* false:不适用事务,设置此变量, 则acknowledgeMode 参数必须设置
			* acknowledgeMode:
				* Session.AUTO_ACKNOWLEDGE:自动消息确认机制
				* Session.CLIENT_ACKNOWLEDGE:客户端确认机制
				* Session.DUPS_OK_ACKNOWLEDGE:有副本的客户端确认消息机制
			*/
			session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
			// 创建目的地,目的地名称即队列的名称。消息的消费者需要通过此名称访问对应的队列
			destination = session.createQueue("helloworld-destination");
			// 创建消息的消费者
			consumer =session.createConsumer(destination);
			// 创建消息对象
			message = consumer.receive();
			//处理消息
			String msg = ((TextMessage)message).getText();
			System.out.println("从 ActiveMQ 服务中获取的文本信息 "+msg);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 回收消息发送者资源
			if (consumer != null) {
				try {
					consumer.close();
				} catch (JMSException e) {
				// TODO Auto-generated catch block
					e.printStackTrace();
				} 
			}
			if (session != null) {
				try {
					session.close();
				} catch (JMSException e) {
				// TODO Auto-generated catch block
					e.printStackTrace();
				} 
			}
			if (connection != null) {
				try {
					connection.close();
				} catch (JMSException e) {
				// TODO Auto-generated catch block
					e.printStackTrace();
				} 
			} 
		} 
	} 
}
  • 测试
    producer
public class Test {
	public static void main(String[] args) {
		HelloWorldProducer producer = new HelloWorldProducer();
		producer.sendHelloWorldActiveMQ("HelloWorld");
	} 
}

consumer

public class Test {
	public static void main(String[] args) {
		HelloWorldConsumer consumer = newHelloWorldConsumer();
		consumer.readHelloWorldActiveMQ();
	}
}

ActiveMQ处理对象信息

能被处理的对象的类必须实现Serializable接口

  • 在生产者代码中的变化
//上面简单实例处理字符串信息时:
// message = session.createTextMessage(msgTest);

//处理对象信息时
//创建消息对象
message = session.createObjectMessage(users);
  • 在消费者代码中的变化
//上面简单实例处理字符串信息时: 
//String msg = ((TextMessage)message).getText();

//处理对象信息时
// 创建消息对象
message = consumer.receive();
//处理消息
ObjectMessage objMessage = (ObjectMessage)message;
Users users = (Users)objMessage.getObject();

JMS - 实现队列服务监听

  • 在生产者代码中无变化
  • 在消费者代码中的变化
//上面简单实例时: 
//String msg = ((TextMessage)message).getText();

//队列服务
consumer.setMessageListener(new MessageListener() {
	//ActiveMQ 回调的方法。通过该方法将消息传递到 consumer
	@Override
	public void onMessage(Message message) {
		//处理消息
		String msg=null;
		try {
			msg = ((TextMessage)message).getText();
		} catch (JMSException e) {
		// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("从 ActiveMQ 服务中获取的文本信息 "+msg);
	}
});

Publish/Subscribe 处理模式(Topic)

  • 在生产者代码中的变化
//上面简单实例时: 
//destination = session.createQueue("helloworld-destination");

//Publish/Subscribe 处理模式
destination = session.createTopic("test-topic");
  • 在消费者代码中的变化
//上面简单实例时: 
//destination = session.createQueue("helloworld-destination");
//String msg = ((TextMessage)message).getText();

//Publish/Subscribe 处理模式
destination = session.createTopic("test-topic");

consumer.setMessageListener(new MessageListener() {
	//ActiveMQ 回调的方法。通过该方法将消息传递到 consumer
	@Override
	public void onMessage(Message message) {
		//处理消息
		String msg=null;
		try {
			msg = ((TextMessage)message).getText();
		} catch (JMSException e) {
		// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("从 ActiveMQ 服务中获取的文本信息 "+msg);
	}
});

//生产者类中重写了run方法
@Override
public void run() {
	this.readHelloWorldActiveMQ();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绿豆蛙给生活加点甜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值