文章目录
RabbitMQ中的MQ是什么?
MQ全称Message Queue(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统之间的通信。发送方称为生产者,接收方称为消费者。
MQ的优势和劣势
优势:
1.应用解耦
2.异步提速
3.削峰填谷
使用了MQ之后,限制消费消息的速度为1000,这样一来,高峰期产生的数据势必会被积压在MQ中,高峰就被“削”掉了,但是因为消息积压,在高峰期过后的一段时间内,消费消息的速度还是会维持在1000,直到消费完积压的消息,这就叫做“填谷”。
使用MQ后,可以提高系统稳定性。
劣势:
1.系统可用性降低
系统引入的外部依赖越多,系统稳定性越差。一旦MQ宕机,就会对业务造成影响。如何保证MQ的高可用?
2.系统复杂度提高
MQ的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过MQ进行异步调用。如何保证消息没有被重复消费?怎么处理消息丢失情况?那么保证消息传递的顺序性?
3.一致性问题
A系统处理完业务,通过MQ给B、C、D三个系统发消息数据,如果B系统、C系统处理成功,D系统处理失败。如何保证消息数据处理的一致性?
使用MQ的满足条件
1.生产者不需要从消费者处获得反馈。引入消息队列之前的直接调用,其接口的返回值应该为空,这才让明明下层的动作还没做,上层却当成动作做完了继续往后走,即所谓异步成为了可能。
2.容许短暂的不一致性。
3.确实是用了有效果。即解耦、提速、削峰这些方面的收益,超过加入MQ,管理MQ这些成本。
常见的MQ
RabbitMQ简介
发展史
AMQP,即 Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。2006年,AMQP规范发布。类比HTTP。
2007年,Rabbit 技术公司基于AMQP标准开发的RabbitMQ 1.0发布。RabbitMQ采用Erlang 语言开发。Erlang 语言由Ericson设计,专门为开发高并发和分布式系统的一种语言,在电信领域使用广泛。
RabbitMQ架构图
JMS的概念
JMS即Java消息服务(JavaMessage Service)应用程序接口,是一个Java平台中关于面向消息中间件的API
JMS是 JavaEE规范中的一种,类比JDBC
很多消息中间件都实现了JMS规范,例如:ActiveMQ。RabbitMQ官方没有提供JMS的实现包,但是开源社区有
RabbitMQ相关概念
Broker:
接收和分发消息的应用,RabbitMQ Server就是 Message Broker
Virtual host:
出于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中,类似于网络中的namespace概念。当多个不同的用户使用同一个RabbitMQ server提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建exchange / queue等
Connection:
publisher / consumer和broker之间的TCP连接
Channel:
如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCP Connection的开销将是巨大的,效率也较低。Channel是在connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和message broker识别 channel,所以channel之间是完全隔离的。Channel作为轻量级的Connection极大减少了操作系统建立TCP connection的开销
Exchange:
message到达 broker的第一站,根据分发规则,匹配查询表中的routing key,分发消息到queue 中去。常用的类型有: direct(point-to-point), topic (publish-subscribe) and fanout (multicast)
Queue:
消息最终被送到这里等待consumer取走
Binding:
exchange和queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息被保存到exchange 中的查询表中,用于message 的分发依据
RabbitMQ的六种工作模式
RabbitMQ提供了6种工作模式:简单模式、work queues、Publish/Subscribe 发布与订阅模式、Routing路由模式、Topics主题模式、RPC远程调用模式(远程调用,不太算MQ)。
1.简单模式
P:生产者,也就是要发送消息的程序
C:消费者:消息的接收者,会一直等待消息到来
queue:消息队列,图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息
生产者:
依赖:
<dependencies>
<!--rabbit java客户端-->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.6.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
生产者代码:
public class ProducerHelloWorld {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("106.15.50.230"); //ip,默认localhost
factory.setPort(5672); //端口 默认5672
factory.setVirtualHost("/myvh"); //虚拟机 默认/
factory.setUsername("hzh"); //用户名 默认guest
factory.setPassword("hzh"); //密码 默认guest
//3.创建连接 Connection
Connection connection = factory.newConnection();
//4.创建 Channel
Channel channel = connection.createChannel();
//5.创建队列
/**
* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
* 参数:
* 1.queue:队列名称
* 2.durable:是否持久化,当目前重启后,还在
* 3.exclusive:
* 是否独占,即只有一个消费者监听端口
* 当Connection关闭时,是否删除队列
* 4.autoDelete:是否自动删除,在没有Consumer时自动删除队列
* 5.arguments:参数
*/
//若没有同名的队列,则会创建,否则不会创建
channel.queueDeclare("hello_world",true,false,false,null);
//6.发送消息
String body = "hello rabbitmq~";
/**
* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
* 参数:
* 1.exchange:交换机名称。简单模式下实参使用""表示使用默认的交换机AMQP
* 2.routingKey:路由的名称
* 3.props:配置信息
* 4.body:发送的消息数据
*/
//使用默认的交换机时,routingKey需要和队列的名称一样才能路由到对应的队列
channel.basicPublish("","hello_world",null,body.getBytes());
//7.释放资源
channel.close();
connection.close();
}
}
消费者:
依赖:
<dependencies>
<!--rabbit java客户端-->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.6.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
消费者代码:
public class ConsumerHelloWorld {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("106.15.50.230"); //ip,默认localhost
factory.setPort(5672); //端口 默认5672
factory.setVirtualHost("/myvh"); //虚拟机 默认/
factory.setUsername("hzh"); //用户名 默认guest
factory.setPassword("hzh"); //密码 默认guest
//3.创建连接 Connection
Connection connection = factory.newConnection();
//4.创建 Channel
Channel channel = connection.createChannel();
//5.创建队列
/**
* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
* 参数:
* 1.queue:队列名称
* 2.durable:是否持久化,当目前重启后,还在
* 3.exclusive:
* 是否独占,即只有一个消费者监听端口
* 当Connection关闭时,是否删除队列
* 4.autoDelete:是否自动删除,在没有Consumer时自动删除队列
* 5.arguments:参数
*/
//若没有同名的队列,则会创建,否则不会创建
channel.queueDeclare("hello_world",true,false,false,null);
//6.消费消息
Consumer consumer = new DefaultConsumer(channel){
/*
回调方法,当收到消息后,会自动执行该方法
参数:
consumerTag:标识
envelope:通过此参数可以获取一些信息,如交换机,路由key。。。
properties:配置文件
body:消息数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System