消息队列是典型的:生产者、消费者模型。生产者不断向消息队列中生产消息,消费者不断的从队列中获取消息。因为消息的生产和消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的侵入,这样就实现了生产者和消费者的解耦。
MQ是消息通信的模型,并不是具体实现。现在实现MQ的有两种主流方式:AMQP、JMS。
两者间的区别和联系:
常见mq产品
RabbitMQ是基于AMQP的一款消息管理系统
官网: http://www.rabbitmq.com/
官方教程:http://www.rabbitmq.com/getstarted.html
官网下载地址:http://www.rabbitmq.com/download.html
安装以linux为例
//以上只是例子,自己下载时注意版本
wget --content-disposition https://packagecloud.io/rabbitmq/erlang/packages/el/7/erlang-22.3.4.12-1.el7.x86_64.rpm/download.rpm
yum localinstall erlang-22.3.4.12-1.el7.x86_64.rpm
wget --content-disposition https://packagecloud.io/rabbitmq/rabbitmq-server/packages/el/7/rabbitmq-server-3.8.13-1.el7.noarch.rpm/download.rpm
//当你下载完成后,你需要运行下面的命令来将 Key 导入
rpm --import https://www.rabbitmq.com/rabbitmq-release-signing-key.asc
yum localinstall rabbitmq-server-3.8.13-1.el7.noarch.rpm
//启动 rabbitmq 服务器,执行命令
systemctl start rabbitmq-server
//设置开机自动启动
systemctl enable rabbitmq-server
//默认情况下,是没有安装web端的客户端插件,需要安装才可以生效
rabbitmq-plugins enable rabbitmq_management
//安装完毕以后,重启服务即可
systemctl restart rabbitmq-server
安装完rabbitmq后,通过ip可进行客户端的访问,端口为15672
安装完rabbitmq后,rabbitmq有一个默认账号和密码是: guest,登录成功后如下图
当然你也可以手动添加用户和密码
rabbitmqctl add_user admin admin //新增用户
rabbitmqctl set_user_tags admin administrator //授权
//当然添加用户和授权也能在客户端手动进行
rabbitmq提供了6种消息模型
:消费者–》消费者,消费和接收有类似的意思,消费者是一个主要用来等待接收消息的用户应用程序
:队列–》rabbitmq内部类似于邮箱的一个概念。虽然消息流经rabbitmq和你的应用程序,但是它们只能存储在队列中。队列只受主机的内存和磁盘限制,实质上是一个大的消息缓冲区。许多生产者可以发送消息到一个队列,许多消费者可以尝试从一个队列接收数据。
:交换机–》交换机一方面:接收生产者发送的消息。另一方面:知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。(三种)
Fanout:广播,将消息交给所有绑定到交换机的队列
Direct:定向,把消息交给符合指定routing key 的队列
Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
就是生产者发送消息到队列,然后消费者监听着队列,一有消息立马接收,一对一关系
work消息模型
work工作模式(资源的竞争)
和简单模式类似,区别就是一个队列有了俩个甚至多个消费者,队列中任务将在他们之间共享,但是一个消息只能被一个消费者获取。
订阅消息模型(3种)
这是一个大类,分为三种,和work的区别就是有了交换机,然后每个消费者会有自己单独的队列,因交换机有三种,故而分三种订阅模式
routing路由模式
topic 主题模式(路由模式的一种)
RPC
略,以后会有rpc的笔记
1)采用workqueue,多个消费者监听同一队列。
2)接收到消息以后,而是通过线程池,异步消费。
默认平均分配,正确的做法应该是消费越快的人,消费的越多,修改方法:
我们可以使用basicQos方法和prefetchCount = 1设置。 这告诉RabbitMQ一次不要向工作人员发送多于一条消息。 或者换句话说,不要向工作人员发送新消息,直到它处理并确认了前一个消息。 相反,它会将其分派给不是仍然忙碌的下一个工作人员。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IkTJYj3t-1665638221923)(C:/Users/gfk/Desktop/xue/乐优/assets/1532765689904.png)]
消息一旦被消费者接收,队列中的消息就会被删除。,RabbitMQ怎么知道消息被接收了呢?
如果消费者领取消息后,还没执行操作就挂掉了呢?或者抛出了异常?消息消费失败,但是RabbitMQ无从得知,这样消息就丢失了!
因此,RabbitMQ有一个ACK机制。当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。不过这种回执ACK分两种情况:
如何选择这需要看消息的重要性:
如何避免消息丢失?
1) 消费者的ACK机制。可以防止消费者丢失消息。
2) 但是,如果在消费者消费之前,MQ就宕机了,消息就没了。
是可以将消息进行持久化呢?
要将消息持久化,前提是:队列、Exchange都持久化
1.交换机持久化
2.队列持久化
3.消息持久化
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
spring:
rabbitmq:
host: 127.0.0.1
username: gfk
password: gfk
virtual-host: /gfk #登录客户端中有其设置,目的是实现用户下几个分支这种作用
@Component
public class Listener {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "spring.test.queue", durable = "true"),
exchange = @Exchange(
value = "spring.test.exchange",
ignoreDeclarationExceptions = "true",
type = ExchangeTypes.TOPIC
),
key = {"#.#"}))
public void listen(String msg){
System.out.println("接收到消息:" + msg);
}
}
@Componet
:类上的注解,注册到Spring容器@RabbitListener
:方法上的注解,声明这个方法是一个消费者方法,需要指定下面的属性:
bindings
:指定绑定关系,可以有多个。值是@QueueBinding
的数组。@QueueBinding
包含下面属性:
value
:这个消费者关联的队列。值是@Queue
,代表一个队列exchange
:队列所绑定的交换机,值是@Exchange
类型key
:队列和交换机绑定的RoutingKey
类似listen这样的方法在一个类中可以写多个,就代表多个消费者。
Spring为AMQP提供了统一的消息处理模板:AmqpTemplate,非常方便的发送消息,其发送方法:
红框圈起来的是比较常用的3个方法,分别是:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class MqDemo {
@Autowired
private AmqpTemplate amqpTemplate;
@Test
public void testSend() throws InterruptedException {
String msg = "hello, Spring boot amqp";
this.amqpTemplate.convertAndSend("spring.test.exchange","a.b", msg);
// 等待10秒后再结束
Thread.sleep(10000);
}
}
运行后查看日志: