Message 消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成, 这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可 能需要持久性存储)等。 Publisher 消息的生产者,也是一个向交换器发布消息的客户端应用程序。
Exchange 交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
Exchange有4种类型:皆可绑定多个队列
direct(默认)【点对点直连,和队列绑定时配置的键 和 消息头的键一样时,发送消息到指定队列】
fanout【扇形,不用管配置的键 或者 消息的键 是什么,直接把消息发給所有绑定的队列】
topic【主题,配置的键可以加 # 或者 * 前者表示匹配0~很多值 后者匹配一个 若消息的键符合配置键的规则,可以发送给队列
headers【低效,和direct差不多,不用】
Queue 消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直 在队列里面,等待消费者连接到这个队列将其取走。
Binding 绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交 换器理解成一个由绑定构成的路由表。 Exchange 和Queue的绑定可以是多对多的关系。
Connection 网络连接,比如一个TCP连接。 【一个客户端只会建立一条连接!!!】
Channel 信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,AMQP 命令都是通过信道 发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都 是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。
Consumer 消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
Virtual Host 虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 /
Broker 表示消息队列服务器实体
4369, 25672 (Erlang发现&集群端口)5672, 5671 (AMQP端口)15672 (web管理后台端口)61613, 61614 (STOMP协议端口)1883, 8883 (MQTT协议端口)
https://www.rabbitmq.com/networking.html
访问地址:http://192.168.190.150:15672/
安装
直接运行下面 第一次启动会下载镜像 docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management docker update rabbitmq --restart=always
配置
依赖使用配置 spring.rabbitmq.host=192.168.190.150 spring.rabbitmq.port=5672 spring.rabbitmq.virtual-host=/ 启动类加注解 @EnableRabbit 配置类【创建消息时,不使用jdk默认序列化器,使用json转换器】 import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyRabbitConfig { @Bean public MessageConverter messageConverter(){ return new Jackson2JsonMessageConverter(); } } org.springframework.boot spring-boot-starter-amqp
自动注入
// 给rabbitmq里面加交换机,队列,绑定,,, @Autowired AmqpAdmin amqpAdmin; // 给rabbitmq发送接收消息 @Autowired RabbitTemplate rabbitTemplate;
创建 交换机
void creatChange() { // 对象:创建 direct类型交换机 // public DirectExchange(String name, boolean durable, boolean autoDelete) // 参数含义:交换机名,是否持久化(关闭后打开rabbitmq有值),是否自动删除 DirectExchange directExchange = new DirectExchange("hello.javaexchange", true, false); amqpAdmin.declareExchange(directExchange); }
创建队列
void creatQueue(){ // public Queue(String name, boolean durable, boolean exclusive, boolean autoDelete, @Nullable Maparguments) // 参数含义:队列名,是否持久化,是否排他(只能同时连一个队列),是否自动删除,指定一些参数(暂时不用) Queue queue = new Queue("hello.javaqueue",true,false,false); amqpAdmin.declareQueue(queue); }
创建绑定
void creadBinding(){ // public Binding(String destination, Binding.DestinationType destinationType, String exchange, String routingKey, @Nullable Maparguments) // 参数含义:目的地,目的地类型[绑定交换机 或者 队列],交换机名,路由键,自定义参数 Binding binding = new Binding("hello.javaqueue", Binding.DestinationType.QUEUE, "hello.javaexchange","hello.java",null); amqpAdmin.declareBinding(binding); }
发送消息
void sendMessage(){ // 交换机名,路由键,要发送的对象(要实现Serializable接口) rabbitTemplate.convertAndSend("hello.javaexchange","hello.java","嘿嘿嘿,喜欢小可爱"); }
接收消息
方法一: 【程序会报错,但是不影响使用】 接口:【不用标什么注解】 public interface ELService { boolean deleteProduct(Message message, ListproductIds, Channel channel); } 实现类: @Service @RabbitListener(queues = {"maimai.product.esqueue"}) public class ELServiceImpl implements ELService { // 直接标 @RabbitHandler 就行,它是根据 方法参数的 @Payload List productIds 来判断由哪个方法处理 @RabbitHandler @Override public boolean deleteProduct(Message message, @Payload List productIds, // 消息中的对象 Channel channel) { System.out.println ("list: "+productIds.toString ()); // 收货,确定收到消息(参数含义:每个消息的标签(按顺序递增,其实就是客户端接收到的第几个消息),是否批量确定) try { channel.basicAck(message.getMessageProperties().getDeliveryTag(),true); } catch (IOException e) { e.printStackTrace (); } // 省略后面代码 } } 方法二: import com.rabbitmq.client.Channel; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; @RabbitListener(queues = {"hello.javaqueue"}) public void recieveMessage(Message message, String content, Channel channel){ // 获得请求体 byte[] body = message.getBody(); // 获得消息头属性 MessageProperties messageProperties = message.getMessageProperties(); System.out.println("接收到的消息:"+message); System.out.println("接收到的对象:"+content); System.out.println("传输数据的通道:"+channel); }
开启confirmCallback,returnsCallback,ack
他们都需要在配置文件中配置,前两个要在配置类中配置。ack需要客户端手动发送
配置文件
# 开启发送端到rabbit的确认回调 spring.rabbitmq.publisher-confirm-type=correlated # 旧的配置,已弃用【是否启用发送端的确认模式,队列接收到消息时的回调】 #spring.rabbitmq.publisher-confirm=true # 开启rabbit到队列的确认回调 spring.rabbitmq.publisher-returns=true # 设置客户端接收到消息的回调【默认是自动ack,这样会发生客户端待机而队列以为客户端已全部接收删除消息的情况】 # 设置为手动确认,当客户端没有回复rabbit,消息不会被删除 spring.rabbitmq.listener.simple.acknowledge-mode=manual
配置类
import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.connection.CorrelationData; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyRabbitConfig { @Autowired RabbitTemplate rabbitTemplate; @Bean public MessageConverter messageConverter(){ return new Jackson2JsonMessageConverter(); } @Override public void confirm(CorrelationData correlationData, boolean b, String s) { System.out.println("confirm...correlationData["+correlationData+"]==>ack:["+b+"]==>cause:["+s+"]"); } }); rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() { @Override public void returnedMessage(Message message, int i, String s, String s1, String s2) { System.out.println("Fail Message["+message+"]==>replyCode["+i+"]" + "==>replyText["+s+"]==>exchange["+s1+"]==>routingKey["+s2+"]"); } }); } }
客户端
- // 收货,确定收到消息(参数含义:每个消息的标签(按顺序递增,其实就是客户端接收到的第几个消息),是否批量确定)
channel.basicAck(deliveryTag,false);
- // 退货,拒绝接收消息
- // 参数含义:每个消息的标签,是否批量处理,是否重新把消息给rabbit(如果为false,丢弃消息)(如果为true,消息入队后还会继续传给客户端,就是传给这个方法)
- channel.basicNack(deliveryTag,false,true);
- // 和上一个的区别是没有批量的选项
- channel.basicReject(deliveryTag,true);
@RabbitListener(queues = {"hello.javaqueue"}) public void recieveMessage(Message message, String content, Channel channel){ // 获得请求体 byte[] body = message.getBody(); // 获得消息头属性 MessageProperties messageProperties = message.getMessageProperties(); System.out.println("接收到的消息:"+message); System.out.println("接收到的对象:"+content); System.out.println("传输数据的通道:"+channel); try { long deliveryTag = message.getMessageProperties().getDeliveryTag(); // 收货,确定收到消息(参数含义:每个消息的标签(按顺序递增,其实就是客户端接收到的第几个消息),是否批量确定) channel.basicAck(deliveryTag,false); // 退货,拒绝接收消息 // 参数含义:每个消息的标签,是否批量处理,是否重新把消息给rabbit(如果为false,丢弃消息)(如果为true,消息入队后还会继续传给客户端,就是传给这个方法) channel.basicNack(deliveryTag,false,true); // 和上一个的区别是没有批量的选项 channel.basicReject(deliveryTag,true); } catch (IOException e) { e.printStackTrace(); } }