初识MQ

同步调用存在的问题

异步调用常见实现就是事件驱动模式

事件驱动模式优势:
优势1:服务解耦
一旦有新业务只需要订阅或者减少事件就行了
优势2:性能提升,吞吐量提高

优势3:服务没有强依赖,不用担心级联失败问题
优势4:流量削峰

异步通信的缺点:
1.依赖于Broker的可靠性,安全性,吞吐能力
2.架构复杂了,业务没有明显的流程线,不好追踪管理
什么是MQ
MQ(MessageQueue),中文是消息队列,字面来看就是存放消息的队列。也就是事件驱动架构中的Broker。

我们选择使用RabbitMQ
RabbitMQ快速入门
RabbitMO是基于Erlang语言开发的开源消息通信中间件,官网地址: https://www.rabbitmq.com/
我们将在centos7虚拟机中用docker来部署
1.下载镜像
在课前资料已经提供了镜像包,上传到虚拟机中后,使用命令加载镜像即可:
docker load -i mq.tar
1.2.安装MQ
执行下面的命令来运行MQ容器:
docker run \
-e RABBITMQ_DEFAULT_USER=itcast \
-e RABBITMQ_DEFAULT_PASS=123321 \
-v mq-plugins:/plugins \
--name mq \
--hostname mq \
-p 15672:15672 \
-p 5672:5672 \
-d \
rabbitmq:3-management
运行完成我们输入地址,就能检查了:

输入我们刚才设置的账号密码


常见消息模型



消费者:
public class ConsumerTest {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接
ConnectionFactory factory = new ConnectionFactory();
// 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
factory.setHost("192.168.150.101");
factory.setPort(5672);
factory.setVirtualHost("/");
factory.setUsername("itcast");
factory.setPassword("123321");
// 1.2.建立连接
Connection connection = factory.newConnection();
// 2.创建通道Channel
Channel channel = connection.createChannel();
// 3.创建队列
String queueName = "simple.queue";
channel.queueDeclare(queueName, false, false, false, null);
// 4.订阅消息
channel.basicConsume(queueName, true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
// 5.处理消息
String message = new String(body);
System.out.println("接收到消息:【" + message + "】");
}
});
System.out.println("等待接收消息。。。。");
}
}
生产者:
public class PublisherTest {
@Test
public void testSendMessage() throws IOException, TimeoutException {
// 1.建立连接
ConnectionFactory factory = new ConnectionFactory();
// 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
factory.setHost("192.168.150.101");
factory.setPort(5672);
factory.setVirtualHost("/");
factory.setUsername("itcast");
factory.setPassword("123321");
// 1.2.建立连接
Connection connection = factory.newConnection();
// 2.创建通道Channel
Channel channel = connection.createChannel();
// 3.创建队列
String queueName = "simple.queue";
channel.queueDeclare(queueName, false, false, false, null);
// 4.发送消息
String message = "hello, rabbitmq!";
channel.basicPublish("", queueName, null, message.getBytes());
System.out.println("发送消息成功:【" + message + "】");
// 5.关闭通道和连接
channel.close();
connection.close();
}
}

SpringAMQP
什么是SpringAMQP


spring-amqp依赖
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

这里要注意,rabbit不会主动创建队列,我们要自己先创建一个队列。这样才能接收到消息
接收消息:

@Component
public class SpringRabbitListener {
@RabbitListener(queues = "simple.queue")
public void listenSimpleQueue(String msg){
System.out.println("消费者接收到simple.queue的消息是:"+msg);
}
}w
我们需要运行消费者的入口程序,然后接收消息

SpringAmqp work queue工作队列


@Test
public void testSendMsg2WorkQueue(){
String queueName="simple.queue";
String message ="hello,msg--";
for (int i = 1; i <= 50; i++) {
rabbitTemplate.convertAndSend(queueName,message+i);
}
}】
修改原来的
public class SpringRabbitListener
两个消费者:
@RabbitListener(queues = "simple.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {
System.out.println("消费者1接收到simple.queue的消息是:"+msg+ LocalTime.now());
Thread.sleep(20);
}
@RabbitListener(queues = "simple.queue")
public void listenWorkQueue2(String msg) throws InterruptedException {
System.err.println("消费者2.......接收到simple.queue的消息是:"+msg+ LocalTime.now());
Thread.sleep(200);
}
虽然两个消费者速度不一样,但是由于消息预取机制,他们分配的量是一样的

发布(publish),订阅(subscribe)

发布订阅:Fanout Exchange




配置:
@Configuration
public class FanoutConfig {
//itcast.fanout
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("itcast.fanout");
}
//fanout.queue1
@Bean
public Queue fanoutQueue1() {
return new Queue("fanout.queue1");
}
//绑定队列1到交换机
@Bean
public Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
}
//fanout.queue2
@Bean
public Queue fanoutQueue2() {
return new Queue("fanout.queue2");
}
@Bean
public Binding fanoutBinding2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
}
}
消费者:

生产者:

@Test
public void testSendFanoutExchange(){
//交换机名称
String exchangeName ="itcast.fanout";
//消息
String message = "hello,every one!";
//发送消息
rabbitTemplate.convertAndSend(exchangeName,"",message);
}
}

发布订阅-DirectExchange



exchange的 type默认是direct
生产者测试代码:
@Test
public void testSendDirectExchange(){
//交换机名称
String exchangeName ="itcast.direct";
//消息
String message = "hello,blue!";
//发送消息
rabbitTemplate.convertAndSend(exchangeName,"blue",message);
}
结果:消费者接收到direct.queue1的消息是:hello,blue!

发布订阅-TopicExchange


1.
@RabbitListener(bindings = @QueueBinding(
value = @Queue("topic.queue1"),
exchange = @Exchange(value = "itcast.topic",type = ExchangeTypes.TOPIC),
key = "china.#"
))
public void listenTopicQueue1(String msg){
System.out.println("消费者接收到topic.queue1的消息是:"+msg);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue("topic.queue2"),
exchange = @Exchange(value = "itcast.topic",type = ExchangeTypes.TOPIC),
key = "#.news"
))
public void listenTopicQueue2(String msg){
System.out.println("消费者接收到topic.queue2的消息是:"+msg);
}
生产者测试方法:
@Test
public void testSendTopicExchange(){
//交换机名称
String exchangeName ="itcast.topic";
//消息
String message = "今天天气真不错";
//发送消息
rabbitTemplate.convertAndSend(exchangeName,"china.news",message);
}
结果是两个都能收到
SpringAMQP -消息转换器


我们的申明直接在PublisherApplication里面做就可以


本文介绍了MQ的基本概念,以RabbitMQ为例展示了如何部署和使用消息队列,包括异步调用的优势和缺点。接着,通过代码示例解释了消费者和生产者的操作,并探讨了SpringAMQP在处理消息队列中的应用,如工作队列、发布/订阅模型以及DirectExchange和TopicExchange的使用。
1028

被折叠的 条评论
为什么被折叠?



