前言:
MQ作为一个消息中间件,在项目中的使用非常广泛。ActiveMQ作为apache出品的中间件,自然也是使用者非常多。
MQ也相当于一个远程调用,HttpClient也是远程调用,他们之间的区别:
HttpClient:适用与对消息的及时性要求较高的地方,请求了必须在短时间内就要响应。
MQ:适用于对消息即时性要求不是特别高的地方,比如发送短信,不需要立马发送,只要在几分钟内发送即可。
一、MQ使用场景介绍:
- 场景一、异步处理。
举个例子:在用户注册成功后,需要向用户发送邮件和短信。
传统方式有两种,串行方式、并行方式。
第一种:串行方式:
第二种:并行方式:
加入MQ之后的流程:
如果每个步骤需要时间为50ms
串行:150ms
并行:100ms
加入MQ50ms
-
场景二、应用解耦:
场景:用户下单后,直接将订单信息存入订单系统,然后直接访问库存系统。
这样做有个缺点:一旦库存系统出现问题,就会使订单系统也出现问题,然后导致下单失败。
加入MQ:
加入了MQ之后,订单系统只需要将信息写入MQ之中。库存系统的状态不会影响下单系统的状态。 -
场景三、流量削锋
场景介绍:某个商品在固定的时间点会进行一个秒杀活动,在那个时间点会有大量的访问请求涌入,如果让这些请求直接访问系统,有可能导致系统的宕机问题。
秒杀系统根据队列中的信息,再做后续处理。 -
场景四、消息通讯:
场景介绍:客户端A和客户端B相互通信。
二、JMS消息服务:
消息队列的JAVAEE规范JMS 。
JMS(Java Message Service,java消息服务)API是一个消息服务的标准/规范,允许应用程序组件基于JavaEE平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。
消息模型
在JMS标准中,有两种消息模型P2P和Pub/Sub。
p2p模式-队列模式
p2p模式中包含三个角色:消息队列、发送者、接收者。每个发送者将消息发送到固定的对列中,消费者从对列中读取消息。对列中保存着消息,直到消息被消费或者超时。
p2p的特点:
-
每个消息只能被一个消费者消费,消费者一旦消费,消息就不会存在对列当中。
-
发送者和消费者在时间上没有依赖性。也就是说不管消费者有没有再运行,发送者都可以将消息发布到对列中。
-
接受者在接收成功后必须向消息队列应答。
Pub/Sub模式–广播/主题模式
Pub/Sub模式中包含三个角色:主题、发布者、订阅者,多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。
Pub/Sub特点: -
每个消息可以有多个消费者。
-
发布者与订阅者有时间上的依赖。订阅者必须在运行中才能接收到发布者发布的消息。
-
为了消费消息,订阅者必须时刻在运行状态。
消息消费方式
在JMS中,消息的产生和消费都是异步的。对于消费来说,JMS的消息者以通过两种方式来消费消息。
- 同步:
订阅者或接收者通过receive方法来接收消息,receive方法在接收到消息之前(或超时之前)将一直阻塞 - 异步
订阅者或接收者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的onMessage方法。
三、接下来讲一讲如何在项目中使用ActiveMQ:
下面使用的是ActiveMQ中的queue队列模式
这里你必须保证自己已经安装好了ActiveMQ,并且成功启动好了
官网:http://activemq.apache.org/
找到解压后的文件:
进行apache-activemq-5.14.0\bin\win64目录 启动activemq.bat 文件。
浏览器访问,出现下面这个页面说明你已经启动成功了。
一、添加pom依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
二、配置文件:
spring:
activemq:
broker-url: tcp://127.0.0.1:61616 //mq的地址
in-memory: false //SpringBoot自带MQ,这里给false,我们使用外部mq。
当然如果使用内部MQ的话,这了给true。就不需要配置地址、user、password
user: admin
password: admin
三、配置类:
/**
* 创建queue和topic对象,交由spring管理
*/
@Configuration
public class ActivemqConfig {
@Bean
public Queue queue(){
return new ActiveMQQueue("注册信息");//队列的名字
}
@Bean
public Topic topic(){
return new ActiveMQTopic("注册信息");
}
}
四、讲消息放入队列中:
@RestController
@RequestMapping("/sms")
public class SmsController {
@Resource
private RedisTemplate redisTemplate;
@Resource
private JmsMessagingTemplate jmsMessagingTemplate; //注入消息管理对象
@Resource
private Queue queue;//注入队列对象
@PostMapping
public ResponseEntity<Object> getCode(@RequestBody TbUser tbUser){
String code = RandomStringUtils.randomNumeric(6);
redisTemplate.opsForValue().set(tbUser.getMobile(),code);
//将发送请求放进activeMQ中
try {
ActiveMQMapMessage mapMessage = new ActiveMQMapMessage();
mapMessage.setString("phone",tbUser.getMobile());
mapMessage.setString("code",code);
jmsMessagingTemplate.convertAndSend(queue,mapMessage);
} catch (JMSException e) {
e.printStackTrace();
System.out.println(e);
}
HashMap<Object, Object> hashMap = new HashMap<>();
BaseResult result = new BaseResult();
result.setErrno(0);
result.setErrmsg("短信发送成功,请注意查收");
hashMap.put("data",result);
return new ResponseEntity<>(hashMap, HttpStatus.OK);
}
}
五、从消息队列中读取消息:
@Component
public class SenConsumer {
@JmsListener(destination = "注册信息")//监听哪个队列
public void send(Message message){
//将message对象转为MapMessage
MapMessage mapMessage = (MapMessage) message;
try {
String phone = mapMessage.getString("phone");
String code = mapMessage.getString("code");
//打印队列中的消息查看
System.out.println(phone);
System.out.println(code);
} catch (Exception e) {
e.printStackTrace();
}
}
}
好了,简单使用到此完毕,相信通过上面的理论介绍和代码演示,你已经初步掌握了ActiveMQ。
看到这里,点个赞再走呀!