一、MQ中间件的使用场景
适合很多变同步为异步的场景(也就是为了效率,要允许请求的接受,与请求相关业务的处理速度不一致)
比如互联网产品搞新用户活动时,注册压力骤增,为了允许注册请求进入的速度与注册请求处理的速度不完全一致,可引入消息队列
某业务在高峰时,如果必须来一个请求,处理一个请求,再接下一个请求(同步),这种情况下效率就比较低
而采用来一个请求,加入队列,即接下一个请求(异步/此时业务未处理),则效率比较高
*变同步为异步主要有两种方式
1.是把高压力业务放进线程,同时执行其他任务,线程有一定的不可靠性
2.是把高压力业务放进MQ队列,同时执行其他任务
二、主流MQ中间件
activeMQ
RabbitMQ
RocketMQ
Kafka
一个中间件,内置了多条队列
三、Spring环境下的整合使用
核心就是配出jmsTemplate对象
也可以不用Spring的jms单独配置<bean class=”消费者”>…<bean class=”生产者”>
四、队列模式和主题模式
Queue模式
-点对点模式
-未被消费者处理的消息不会丢失
-被消费者处理的消息会删除
-被消费者A处理的消息,消费者B不会再接收到,消费者B会去接收下一条消息
Topic主题模式(发布者订阅者模式)
-广播模式(发布者发布消息,所有在线的订阅者都能收到,未在线的订阅者,消息丢失)
切换topic监听器
这是广播式的:
订阅者必须比发布者先启动
五、主动式消费和监听式消费
主动式:同步消费,监听式:异步消费
监听式消费一般用于多线程两个系统,监听式消费是最常用的消费模式
消费者模式:只有一个消费者能消费
六、模拟异步确保分布式事务
试验:spring框架下+activeMQ
首先配置:spring-amq
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jms="http://www.springframework.org/schema/jms"
xmlns:amq="http://activemq.apache.org/schema/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms-4.2.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core-5.10.0.xsd">
<!-- ActiveMQ连接工厂 -->
<amq:connectionFactory id="amqConnectionFactory"
brokerURL="tcp://localhost:61616" userName="admin" password="admin" />
<!-- SpringCaching连接工厂 -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="amqConnectionFactory"></property>
<property name="sessionCacheSize" value="100" />
</bean>
<!-- 定义JmsTemplate的Queue类型 -->
<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
<constructor-arg ref="connectionFactory" />
<property name="pubSubDomain" value="false" />
</bean>
<!-- 定义JmsTemplate的Topic类型 -->
<bean id="jmsTopicTemplate" class="org.springframework.jms.core.JmsTemplate">
<constructor-arg ref="connectionFactory" />
<property name="pubSubDomain" value="true" />
<property name="receiveTimeout" value="30000" />
</bean>
<!-- 定义Queue监听器 -->
<jms:listener-container destination-type="queue"
container-type="default" connection-factory="connectionFactory"
acknowledge="auto">
<!-- <jms:listener destination="testQ" ref="testQListener"></jms:listener> -->
</jms:listener-container>
<!-- 定义Topic监听器 -->
<jms:listener-container destination-type="topic"
container-type="default" connection-factory="connectionFactory"
acknowledge="auto">
<jms:listener destination="testq" ref="testQListener"></jms:listener>
</jms:listener-container>
</beans>
xml里加上spring-amq.xml
jmsQueueTemplate和jmsTopicTemplate是两种模式
jmsQueueTemplate:点对点模式的消息,只能被一个消费者接收;
jmsTopicTemplate:发布订阅模式的消息,可以被多个订阅者同时接收
@Qualifier("jmsQueueTemplate")指定使用哪种模式
没有配置监听器的时候,消息进入队列后,需要手动取出
(配置监听器器的时候,ref="testQListener",类名首字母要小写)
package com.easywork.pp.controller;
import com.easycore.utils.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
@Controller
@RequestMapping("/mq")
public class MqController extends BaseController {
@Autowired
@Qualifier("jmsQueueTemplate")
private JmsTemplate jmsTemplate;
@RequestMapping("/addToMq")
@ResponseBody
public String addToMq(String task){
MessageCreator creator = new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage msg = session.createTextMessage(task);
return msg;
}
};
//设置一条队列
jmsTemplate.send("testQ",creator);
System.out.println("task:"+task);
return "success";
}
//手动从队列中取出
@RequestMapping(value = "/getFromMq",produces ={"text/html;charset=utf-8"} )
@ResponseBody
public String getFromMq() throws JMSException {
Message message = jmsTemplate.receive("testQ");
String txt = ((TextMessage) message).getText();
return txt;
}
}
spring-amq里配上监听器之后,就不需要手动取出了
新建一个ma包
package com.easywork.pp.mq;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@Component
public class TestQListener implements MessageListener {
@Override
public void onMessage(Message message) {
TextMessage msg = (TextMessage) message;
try {
System.out.println("监听式消费:"+msg.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
在spring-amq里配上listener
<!-- 定义Queue监听器 -->
<jms:listener-container destination-type="queue"
container-type="default" connection-factory="connectionFactory"
acknowledge="auto">
<!-- <jms:listener destination="testQ" ref="testQListener"></jms:listener> -->
</jms:listener-container>
<!-- 定义Topic监听器 -->
<jms:listener-container destination-type="topic"
container-type="default" connection-factory="connectionFactory"
acknowledge="auto">
<jms:listener destination="testA" ref="testQListener"></jms:listener>
</jms:listener-container>
这样就会监听并消费了
jmsQueueTemplate:点对点模式的消息,只能被一个消费者接收;
jmsTopicTemplate:发布订阅模式的消息,可以被多个订阅者同时接收
七、总结消息队列MQ
1.跑MQ服务
2.Spring里配置生产者和消费者
生产者
-可以是MQ自身的<bean class=”….Producer”>
-也可以是Spring的JMS<bean class=”…..JmsTemplate”>
-生产者可以是Queue模式,也可以是Topic模式
消费者(监听式消费)
-配置Listener指向自定义的Listener类,重写onMessage(msg)方法,参数就是监听到的消息
-可以监听队列模式/主题模式下的某个信道(destination)
3.MQ的几大用途
-可以使业务请求与业务处理不同步,增强并发性能。
-用于系统与系统之间的通信(分布式系统中多个微服务之间的通信)
-异步确保型事务(分布式事务的一种)