同步调用和异步调用
1.同步调用(类似视频通话,只能接一个的视频通话)
优点:时效性较强,可以立即得到结果
微服务间基于Feign
的调用属于同步方式
存在问题:
- 耦合度高(每次加入新的需求,都要修改原来的代码)
- 性能下降(调用者需要等待服务提供者响应)
- 资源浪费(调用链中每个服务在等待响应过程中,不能释放请求占用的资源)
- 级联失败(如果服务提供者出现问题,所以调用方都会跟着出问题)
2.异步调用(类似微信聊天,可以接收多个人发的消息)
常见的实现:事件驱动模式
优点:
- 耦合度低
- 性能提升,吞吐量提高
- 服务没有强依赖,不担心级联失败问题
- 流量削峰
缺点:
- 依赖于
Broker
的可靠性、安全性、吞吐能力 - 架构复杂了,业务没有明显的流程线,不好追踪管理
MQ
一.了解MQ
存放消息的队列,也就是事件驱动架构中的Broker
RabbitMQ | ActiveMQ | RocketMQ | Kafka | |
---|---|---|---|---|
公司/社区 | Rabbit | Apache | 阿里 | Apache |
开发语言 | Erlang | Java | Java | Scala&Java |
协议支持 | AMQP,XMPP,SMTP,STOMP | OpenWire,STOMP, REST,XMPP,AMOP | 自定义协议 | 自定义协议 |
可用性 | 高 | 一般 | 高 | 高 |
单机吞吐量 | 一般 | 差 | 高 | 非常高 |
消息延迟 | 微秒级 | 毫秒级 | 毫秒级 | 毫米以内 |
消息可靠性 | 高 | 一般 | 高 | 一般 |
二.RabbitMQ
快速入门
1.单机部署RabbitMQ
在Centos7
虚拟机中使用Docker
来安装
1.1下载镜像
开启Centos7
虚拟机,使用XShell
连接
#启动docker
sudo systemctl start docker
#在线拉取RabbitMQ
sudo docker pull rabbitmq:3-management
1.2安装MQ
执行下面命令来运行MQ
容器:
sudo docker run -d \
-e RABBITMQ_DEFAULT_USER=itcast \
-e RABBITMQ_DEFAULT_PASS=123321 \
--name mq \
--hostname mql \
-p 15672:15672 \
-p 5672:5672 \
rabbitmq:3-management
注意:若出现如下报错
[roo@localhost ~]$ sudo systemctl start docker
[sudo] roo 的密码:
[roo@localhost ~]$ sudo docker run -d \
> -e RABBITMQ_DEFAULT_USER=itcast \
> -e RABBITMQ_DEFAULT_PASS=123321 \
> --name mq \
> --hostname mql \
> -p 15672:15672 \
> -p 5672:5672 \
> rabbitmq:3-management
docker: Error response from daemon: Conflict. The container name "/mq" is already in use by container "741a06aa779f2589174a5d5da7de026b6af9a922c00d53610c9868b2e507e58c". You have to remove (or rename) that container to be able to reuse that name.
See 'docker run --help'.
是因为一个名为mq
的容器在运行了,则执行一下命令后,再执行RabbitMQ容器启动命令:
sudo docker stop mq
sudo docker rm mq
访问虚拟机对外IP:15672,我的是【http://192.168.174.129:15672/】
账号:itcast
密码:123321
三.常见消息模型
- 基本消息队列(
BasicQueue
) - 工作消息队列(
BasicQueue
)
- 发布订阅(
Publish
、Subscribe
),又根据交换机类型不同分为三种:- Fanout Exchange:广播
- Direct Exchange:路由
- Topic Exchange:主题
1HelloWord
:代码方式
只包括三个角色:
//发布者
public class PublisherTest {
@Test
public void testSendMessage() throws IOException, TimeoutException {
// 1.建立连接
ConnectionFactory factory = new ConnectionFactory();
// 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
factory.setHost("192.168.174.129");
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();
}
}
先运行发布者的代码,hello,rabbitmq!
缓存在消息队列中
//消费者
public class ConsumerTest {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接
ConnectionFactory factory = new ConnectionFactory();
// 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
factory.setHost("192.168.174.129");
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("等待接收消息。。。。");
}
}
再运行消费者的代码后,消息队列中的hello,rabbitmq!
就消失了(异步)