1.前言
了解同步调用和异步调用
1.1.同步调用
比如这里的支付服务,需要等待订单服务、短信服务…执行完毕才能执行,这样支付整个流程完毕需要500ms
然后如果订单、仓储等其中一个服务挂掉了,那么支付服务请求请求不了,挂掉的服务越来越多,级联失败;——>服务提供者出现问题,那么整个微服务都会出现故障;
资源浪费
:消费者需要等待服务提供者响应,不能干其他事情只能干等;
耦合度较高
:每次加入新的需求,还需要动原来的代码——>比如:订单加了一个用户积分,那么Feign模块中还要加相关代码,以便订单模块调用,订单模块还需要+调用用户积分的代码:需要改动订单代码;
再来一个场景:比如说,我们要删除某个业务,需要将支付服务中的调用某个业务的相关代码删除;
缺点
:
1.2.异步调用
利用了一个Broker进行事件通知
优点
:
级联失败问题得到解决:比如订单服务挂了,它并不会影响支付服务,自己重启就行了
耦合度低:因为支付服务不需要调用其他服务了,而是通过发布事件,Broker对其他事务进行通知,你支付服务该干嘛干嘛,发布完消息返回用户支付成功
——>支付服务发布支付成功消息给到Broker,然后Broker进行通知——>对其他服务,然后其他服务执行即可;
吞吐量提升:因为不存在服务之间的调用,所以时间较少(发布信息),能发送的数据就能够越多——>牺牲:消息的延迟,从发送到接收
流量削峰:Broker作为缓冲,将并发请求保存起来,然后通过微服务里面的实现的功能,对请求进行放行——>从而达到流量削峰的效果
缺点
:
异步调用
只是通知你干一件事情,并不知道你干没干完(通过Broker发送请求让你干事情,从而达到解耦的效果)
流量削峰
:
Broker作为缓冲区对请求进行缓存;
2.认识MQ
RabbitMQ:一般用于业务之间的通信,安全稳定。
KafKa:一般用于海量数据处理,单机吞吐量高:每秒钟能发送的数据大小来分析
3.RabbitMQ入门
3.1 安装使用
1.导入虚拟机并解压
2.运行镜像产生MQ容器
3.运行成功
4.访问RabbitMQ
5.RabbitMQ各属性解读
发布者Publisher
会把消息->给到交换机(Exchange)
,交换机路由给到指定的消息队列(Queue)
,queue把消息保存起来,然后消费者consumer会从queue将消息拿出来
VirtualHost:各个虚拟主机是不一样的,相当于对操作进行分组——>不同用户访问是不同的虚拟主机,体现隔离性;
3.2 常见消息模型
3.3 简单队列案例
3.3.1 发送
1.建立消息队列连接
IDEA中写好配置
package cn.itcast.mq.helloworld;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import org.junit.Test;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class PublisherTest {
@Test
public void testSendMessage() throws IOException, TimeoutException {
// 1.建立连接
ConnectionFactory factory = new ConnectionFactory();
// 1.1.设置连接参数,分别是:主机名、消息通信端口号、vhost、用户名、密码
factory.setHost("192.168.88.130");
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();
}
}
运行代码后
进入RabbitMQ发现连接成功
2.创建一个通道,以此发送消息到队列
3.利用通道来声明队列
4.发送消息
5.消息成功发送到队列中,消息断开,不管你后面的接收
3.3.2 接收
老样子 IDEA中写好配置,运行即能接收
package cn.itcast.mq.helloworld;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ConsumerTest {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接
ConnectionFactory factory = new ConnectionFactory();
// 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
factory.setHost("192.168.88.130");
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("等待接收消息。。。。");
}
}
跟消息队列的消息发送流程
差不多,注意点就是也声明了队列:避免队列不存在
还有一点:定义了consume
的消费行为handleDelivery()
,消费行为和队列名字绑定了
,只要一有消息就会传递到队列当中被消费行为执行;——>一个异步操作,消费者与消息提供者互相不等待
从先执行后面的打印信息,再执行前面的接收到消息可以看出是一个异步操作
:接没接受到不管 (两个线程一个回调,一个往后面继续走
消费者收到消息后,RabbitMQ中的消息队列的消息就没有了