Java message service的基本概念及RabbitMQ的实现
- 一、JMS(Java message service)的概念
- 1、producer 生产者
- 2、consumer 消费者
- 3、JMP的可靠性
- 三、RabbitMQ
- 1、RabbitMQ的概念
- 2、Springboot集成RabbitMQ
- 3、定义JMSService接口
- 四、用RabbitMQ实现收发送消息
- 目标
- 知识点
- 1、定义api模块
- 2、定义jms-rabbitmq模块
- 2.1、定义RabbitMQDeclare实现
- 2.2、定义SMSConfig配置类
- 2.3、定义发送实现
- 2.4 定义接收实现
- 2.5 RabbitMQ自定义配置类
一、JMS(Java message service)的概念
1、producer 生产者
producer指的是消息发起方,要投递消息的那端,如果投递的是一对一的队列消息,那么它就是队列发送人,如果投递的是一对多的发布/订阅类型,那么它就是主题发布者
2、consumer 消费者
就是消息的接收方,如果递交的是一对一的队列,那么它是队列的接收者,如果投资的是一对多的发布/订阅类型,那么它就是主题订阅者
3、JMP的可靠性
持久化:服务器宕机时会将数据保存到本地,可保证未被发送的消息在消费前不会丢失,亦不会重复发送、重复消费
事务支持ACID:
producer的事务发送:发送一组消息直止commit为止,如果中途有故障或某些原因产生rollback则这批消息不会被发送,从而保证事务完整性
consumer的事务接收:consumer要么整批接收要么一条也不收。
ACK确认机制: 事务和ack都是消息确认的方式,同时存在时事务的优先级要高一些,开启事务时ack是无效的,非事务的模式下,ack开启才有效。
三、RabbitMQ
JMS只是消息服务的一个协议,而实现却有好几家,如ActiveMQ,RabbitMQ,RocketMQ等。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fAJ2936E-1641777846170)(https://static01.imgkr.com/temp/e0415dc61159447e989a41955956b6d4.png)]
1、RabbitMQ的概念
RabbitMQ的核心概念包括:生产者、消费者、消息模型(虚拟主机、消息通道、交换机、路由、队列),生产者、消费者前面已讲过,现在讲一下消息模型里面的三个概念:
虚拟主机(vhost):
虚拟主机:一个虚拟主机持有一组交换机、队列和绑定。虚拟主机的作用在于进行权限管控,rabbitmq默认有一个虚拟主机"/"。可以使用rabbitmqctl add_vhost命令添加虚拟主机,然后使用rabbitmqctl set_permissions命令设置指定用户在指定虚拟主机下的权限,以此达到权限管控的目的。
消息通道(channel):
消息通道: 在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。
交换机: 消息的运输者(快递员)
交换机: exchange的功能是用于消息分发,它负责接收消息并转发到与之绑定的队列,exchange不存储消息,如果一个exchange没有binding任何Queue,那么当它会丢弃生产者发送过来的消息,在启用ACK机制后,如果exchange找不到队列,则会返回错误。一个exchange可以和多个Queue进行绑定。
交换机有四种类型:
路由模式(Direct):
direct 类型的行为是"先匹配, 再投送"。即在绑定时设定一个 routing_key, 消息的routing_key 匹配时, 才会被交换器投送到绑定的队列中去。direct是rabbitmq的默认交换机类型。
通配符模式(Topic):
类似路由模式,但是routing_key支持模糊匹配,按规则转发消息(最灵活)。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。
发布订阅模式(Fanout):
转发消息到所有绑定队列,忽略routing_key。
Headers:
设置header attribute参数类型的交换机。相较于 direct 和 topic 固定地使用 routing_key , headers 则是一个自定义匹配规则的类型,忽略routing_key。在队列与交换器绑定时, 会设定一组键值对规则, 消息中也包括一组键值对( headers 属性), 当这些键值对有一对, 或全部匹配时, 消息被投送到对应队列。
在绑定Queue与Exchange时指定一组键值对,当消息发送到RabbitMQ时会取到该消息的headers与Exchange绑定时指定的键值对进行匹配。如果完全匹配则消息会路由到该队列,否则不会路由到该队列。headers属性是一个键值对,可以是Hashtable,键值对的值可以是任何类型。
匹配规则x-match有下列两种类型:
x-match = all :表示所有的键值对都匹配才能接受到消息
x-match = any :表示只要有键值对匹配就能接受到消息
路由: 消息的运输途径(列车?汽车?飞机?)
队列: 消息的运输路线(从东莞到武汉)
以快递为例,陈大文要从东莞快递一套13香给武汉的小明,那么:
陈大文(producer)把13香(消息)打包给快递员(交换机),快递员把包裹拿回总部然后根据要求选择了从东莞送到武汉(队列)的飞机(路由)路线,
最终小明(consumer)收到包裹。
2、Springboot集成RabbitMQ
加上依赖:
<!-- rabbit mq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
3、定义JMSService接口
因为要考虑到日后可以会将RabbitMQ换成其他厂的MQ,所以一定要采用接口方式
四、用RabbitMQ实现收发送消息
上面已经搭好框架,现在基于这个框架做一个自定义类SMS的发送与接收例子。
原理:先在application.yml里面定义
目标
1、rabbitmq的集成;
2、定义两组rabbitmqtemplate,一组名为AckRabbitTemplate的发送时会触发confirm/return回调(用于严谨事务处理);
别一组RabbitTemplate则不触发回调,自动ack.
3、消费端亦能实现某些消费可以自动ack,某些消费要手动ack
知识点
application.yml里面的publisher-returns、publisher-confirm-type只是针对producer的参数;listener参数只是针对consumer端
RabbitTemplate.waitForConfirms和rabbitmq的confirm、return回调只是针对producer->broker端,但凡它将消息送至broker的交换机就会call confirm,
如果交换机没能将消息投入队列,则会call return. 这一切讲的是producer和broker的事,和consumer没一毛钱关系
Consumer的ack也是同样道理,它只是consumer和mq broker之间的确认,和producer没一毛钱关系
consumer方需要ack的若没有ack确认,则在Broker界面会看到unacked累加,一旦consumer方关闭,这个unacked就会转成ready(待发到consumer),consumer重启后就会又收到这批未ack的消息
@RabbitListener可以通过参数指明是自动ack还是手动ack,这个参数比application.yml的listener设置的优先度高
若同一消息发出能多条队列同时接收,用direct方式的交换机也能实现,那就是将多个队列绑定到同一个交换机同一个routerkey即可
1、定义api模块
将调用规范、api接口、pojo数据结构还有一些常量类抽离放在api 模块中。
2、定义jms-rabbitmq模块
此模块是用RabbitMQ方式收发sms消息
2.1、定义RabbitMQDeclare实现
这个是JMSService接口的RabbitMQ实现,包括交换机、队列的通用操作。
2.2、定义SMSConfig配置类
它的作用是app初始化后,用来创建收发SMS所需要的交换机、队列
2.3、定义发送实现
com.freestyle.jmssample.rabbitmq.service.SendSMSServiceImp
2.4 定义接收实现
com.freestyle.jmssample.rabbitmq.service.ReceiveSMSServiceImp
2.5 RabbitMQ自定义配置类
com.freestyle.jms.common.RabbitMQConfig
代码链接,觉得有用的话请点个Star: https://gitee.com/tigera15/jms-samples/tree/master