RabbitMQ笔记

        RabbitMQ提供了6种模式:Direct简单模式(点对点),work模式,Publish/Subscribe发布与订阅模式,Routing路由模式,Topics主题模式(通配符匹配),RPC远程调用模式。

生产者发消息给Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!

工作原理:

docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 -p 61613:61613 -p 61614:61614 -p 1883:1883 -p 8883:8883 rabbitmq:management

docker update rabbitmq --restart=always

4369,25672 (Erlang发现&集群端口)

5672,5671(AMQP端口)

15672 (Web管理后台端口)

61613, 61614 (STOMP协议端口)

1883,,8883, (MQTT协议端口)

默认登录地址: http://192.168.56.10:15672 guest/guest

Exchange有常见以下3种类型:

Fanout:广播,将消息交给所有绑定到交换机的队列

Direct:定向(点对点),把消息交给符合指定routing key 的队列(注意:如果绑定的时候不指定routingKey为空,交换机发送消息时(不指定routingKey为空)会广播发送到所有绑定(routingKey为空)的Queue)

Topic:通配符(部分广播),把消息交给符合routing pattern(路由模式) 的队列

Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符!

Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert

binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)

通配符规则:exchange 与 queue 绑定的时候可以使用通配符;发送消息的时候不能使用通配符

#:匹配一个或多个词 eg: item.#:能够匹配item.insert.abc 或者 item.insert

*:匹配1个词 eg:  item.*:只能匹配item.insert

其他设置: Type- Durable 持久化 Auto delete-NO 不允许自动删除; Iternal -No

Queue队列设置: Type   Durability是否持久化 

Bindings 绑定设置- To queue - Routing key-可以使用通配符

发消息是发给exchanges交换机;监听的是queues队列

创建Queue时注意到可配置为持久化(Durable)和非持久化(Transient),当Consumer不在线时,持久化的Queue会暂存消息,非持久化的Queue会丢弃消息。

--------------------------------------------------------------------------------------------------------------

生产者发送消息

生产者创建连接(Connection),开启一个信道(Channel),连接到RabbitMQ Broker;

声明队列并设置属性;如是否排它,是否持久化,是否自动删除;

将路由键(空字符串)与队列绑定起来;

发送消息至RabbitMQ Broker;

关闭信道;

关闭连接;

消费者接收消息

消费者创建连接(Connection),开启一个信道(Channel),连接到RabbitMQ Broker

向Broker 请求消费相应队列中的消息,设置相应的回调函数;

等待Broker回应闭关投递响应队列中的消息,消费者接收消息;

确认(ack,自动确认)接收到的消息;

RabbitMQ从队列中删除相应已经被确认的消息;

关闭信道;

关闭连接;

---------------------------------------------------------------------------------------------------------------

RabbitMQ消息确认机制--可靠抵达-保证消息不丢失

  • publisher confirmCallback 确认模式  P--->Exchange
  • publisher returnCallback 未投递到queue退回模式  Exchange--->queue
  • consumer ack 机制  queue--->Consumer

P ------->  Exchange  ---------> queue   --------->Consumer

#开启发送端消息确认机制-回调函数
#rabbitMq服务器地址
spring.rabbitmq.addresses=192.168.191.35
#使用的Virtual host名称
spring.rabbitmq.virtual-host=test

#开启发送端确认-到达服务器Exchange
#spring.rabbitmq.publisher-confirms=true
#高版本代替上面配置,确认消息已发送到交换(Exchange)选择确认消息类型为交互
spring.rabbitmq.publisher-confirm-type=correlated
#开启发送端确认-消息抵达队列Queue的确认
spring.rabbitmq.publisher-returns=true
#只要抵达队列,以异步方式优先回调returnConfirm回调函数
spring.rabbitmq.template.mandatory=true

#客户端消费-手动ack确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual
rabbit配置序列化json--消息确认机制
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
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;

import javax.annotation.PostConstruct;
import java.util.HashMap;

@EnableRabbit
@Configuration
public class MyRabbitmqConfig {
     // 使用json序列化机制,进行消息转换
    @Bean
    public MessageConverter messageConverter() {
        //在容器中导入Json的消息转换器
        return new Jackson2JsonMessageConverter();
    }
    @Autowired
    RabbitTemplate rabbitTemplate;

    /**定制rabbitTemplate 具有消息确认机制回调函数
     * A,服务器收到消息就回调ConfirmCallback
         * 1,spring.rabbitmq.publisher-confirms=true  开启发送端确认
            #高版本代替上面配置,确认消息已发送到交换(Exchange)选择确认消息类型为交互
            spring.rabbitmq.publisher-confirm-type=correlated
         * 2,设置确认回调--服务端收到消息就会自动触发回调函数--只要消息抵达服务器Exchange其中ack=true
     * B, 消息正确抵达队列进行回调
         * 1,spring.rabbitmq.publisher-returns=true #开启发送端确认-消息抵达队列Queue的确认
            * spring.rabbitmq.template.mandatory=true  #只要抵达队列,以异步方式优先回调returnConfirm回调函数
         * 2,失败才回调returnCallback

     * C,客户端消费端确认(保证每个消息被正确消费,此时才可以删除Queue队列中这个消息)
     * 1,默认是自动确认的,只要消息接收到,客户端会自动确认,服务端就会移除这个消息
     *   问题: 收到很多消息,自动回复给服务器ack,只有一个消息处理成功,宕机了,发生消息丢失
     *   需要手动确认-执行完成一个确认一个;只要没有ack,消息一致是unacked转态,即使Consumer宕机,消息也不丢失,
     *   会重新变为ready转态,下一次Consumer连接继续执行
     *   spring.rabbitmq.listener.simple.acknowledge-mode=manual #客户端消费-手动ack确认
     *2,如何确认签收:
         * long deliveryTag = message.getMessageProperties().getDeliveryTag();//channel中按顺序自增的
         * channel.basicAck(deliveryTag,false);//成功-手动确认签收 false-只签收当前的, true-批量确认签收
         *  channel.basicReject(deliveryTag,true);//失败-拒绝签收
* //long deliberyTag, boolean multiple,boolean requeue //第3个参数:requeue=false丢弃;requeue=true发回服务器重新入队
         *   channel.basicNack(deliveryTag,false,true);//失败-拒签
     */
    @PostConstruct //对象创建完成之后执行
    public void initRabbitTemplate(){
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {//设置确认回调
            /**
             * 服务器代理Exchange 收到消息之后会自动调用
             * @param correlationData 当前消息的唯一关联数据(消息的唯一Id)
             * @param ack 消息是否成功收到: ack=true 表示成功  ack=false 表示失败
             * @param cause 失败原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {

            }
        });
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * 消息抵达队列Queue的确认回调,只要消息没有投递到指定的队列就触发这个失败回调
             * @param message 投递失败的消息详情信息
             * @param replyCode 回复状态码
             * @param replyText 回复文本内容
             * @param  exchange 消息发送那个交换机
             * @param  routingKey 消息使用的路由键
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("失败执行--fail---message="+message+"--replyCode="+replyCode+"--replyText="+
                        replyText+" --exchange"+exchange+"--routingKey="+routingKey);
            }
        });

    }

    //创建交换机-主题类型的change
     @Bean
    public Exchange topicExchange() {
        /**
         *   String name,
         *   boolean durable,  是否持久化
         *   boolean autoDelete, 是否允许自动删除
         *   Map<String, Object> arguments
         */
        return new TopicExchange("test.exchange", true, false);
    }
    /**
     * 创建Queue时注意到可配置为持久化(Durable)和非持久化(Transient),
     * 当Consumer不在线时,持久化的Queue会暂存消息,非持久化的Queue会丢弃消息。
     * 普通队列
     * @return
     */
    @Bean
    public Queue dataCreateQueue() {
        Queue queue = new Queue("test.hys.data", true, false, false);
        return queue;
    }

    /**
     * 创建用户队列
     * @return
     */
    @Bean
    public Queue userCreateQueue() {
        Queue queue = new Queue("test.hys.user", true, false, false);
        return queue;
    }

    /**
     * 交换机绑定队列
     * 如果绑定的时候不指定routingKey为空,交换机发送消息时(不指定routingKey为空)会广播发送到所有绑定(routingKey为空)的Queue
     *
     * 创建binding
     * @return
     */
    @Bean
    public Binding dataCreateBinding() {
        /**
         * String destination, 目的地(队列名或者交换机名字) --test.hys.data
         * DestinationType destinationType, 目的地类型(Queue、Exhcange)
         * String exchange,  --test.exchange
         * String routingKey,  --test.*.data
         * Map<String, Object> arguments
         * */
        return new Binding("test.hys.data", Binding.DestinationType.QUEUE, "test.exchange", "test.*.data", null);
    }

    //交换机与队列 绑定
     @Bean
    public Binding userCreateBinding() {
        /**
         * String destination, 目的地(队列名或者交换机名字) 
         * DestinationType destinationType, 目的地类型(Queue、Exhcange)
         * String exchange,  
         * String routingKey,  
         * Map<String, Object> arguments
         * */
        return new Binding("test.hys.user", Binding.DestinationType.QUEUE, "test.exchange", "test.#", null);
    }


}

从队列中接受消息:

1,一个队列中同一消息可以很多人都监听,只能有一个客户端收到之后队列删除消息

2,只有一个消息完全处理完成之后,方法运行结束,才可以接收下一个消息

/**
     * 1、多个人监听同一个队列,谁收到消息?
     *      只有一个人能收到消息。(消息重复消费)
     *
     * 2、service监听消息队列里面的内容,方法参数能写哪些?
     *      Map<String,Object> content:自定义对象;将队列的内容自动的转为这个对象
     *      Message message:可以获取到当前消息的详细信息;(消息的id,什么时候发送的....)
     *      Channel channel:通道,可以ack数据
     *      参数不分先后顺序,不一定全要或者要哪些
     *  3、切换到手动ack模式
     
     *  4、我们如果没有回复消息;
     *      1)、我们一直在线:此消息不会被再发给别人;
     *      2)、一旦掉线,未回复的消息又会变成ready;又可以发给别人
     * @param content
     */
    @RabbitListener(queues = "myqueue")
    public void hello(Message message,Map<String,Object> content,  Channel channel) throws IOException {
        System.out.println("hello..."+content);


        /**
         * long deliveryTag,消息的标签
         * boolean multiple,是否批量拒绝多个消息
         * boolean requeue,是否重新将消息放回队列,false【discarded【丢弃】/dead-lettered[死信]】
         */
        //1、拒绝(unack)不入队,消息直接被丢弃
        //2、拒绝(unacke)入队。消息会被立即重新投递。
        //3、以前的不回复。消息是unacked状态,如果consumer断了。unacked变成ready成能下一次重新投递
//        channel.basicNack(message.getMessageProperties().getDeliveryTag(),
//                false,
//                true);

        //4、拒绝(reject)不入队,和1一样
        //5、拒绝(reject)入队。消息会被立即重新投递。
       // channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);

        //6、消息会被移除
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);

        //没响应的回复
        //我不要的回复

    }
标注注解的组件必须在spring容器中
@EnableRabbit  监听队列消息的时候必须加上这个注解
@RabbitListener :类+方法上,
@RabbitHandler 标在方法上,(重载区分不同的消息-队列中存放不同的对象数据);
import com.rabbitmq.client.Channel;
import io.niceseason.gulimall.order.entity.OrderEntity;
import io.niceseason.gulimall.order.service.OrderService;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;

@Component
@RabbitListener(queues = {"order.release.order.queue"})
public class OrderCloseListener {

    @Autowired
    private OrderService orderService;
   /**
     * @param orderEntity 发送的消息类型
     * @param message 原生消息详细信息:头+体
     * @param channel 当前传输的通道
     * @throws IOException
     */
    @RabbitHandler
    public void listener(OrderEntity orderEntity, Message message, Channel channel) throws IOException {
        System.out.println("收到过期的订单信息,准备关闭订单" + orderEntity.getOrderSn());
        long deliveryTag = message.getMessageProperties().getDeliveryTag();//channel中按顺序自增的
        try {
            orderService.closeOrder(orderEntity);
            channel.basicAck(deliveryTag,false);//手动确认签收 false-只签收当前的, true-批量确认签收
        } catch (Exception e){
            channel.basicReject(deliveryTag,true);//拒绝签收
            //long deliberyTag, boolean multiple,boolean requeue //第3个参数:requeue=false丢弃;requeue=true发回服务器重新入队
            channel.basicNack(deliveryTag,false,true);//拒签二
        }

    }

@RabbitHandler
    public void handleStockLockedRelease(StockLockedTo stockLockedTo, Message message, Channel channel) throws IOException {
        log.info("************************收到库存解锁的消息********************************");
        try {
            wareSkuService.unlock(stockLockedTo);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
        }
    }
}
 @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 向rabbitMq中发送数据
     * 交换机名称:test.topic.exchange
     * 路由键: test.hys.data
     * 发送的消息数据:answer
     * 
     *  queue名称         binding-key通配符
     * test.hys.data     test.hys.data	  匹配
     * test.hys.org      test.*.org	不匹配
     * test.hys.user     test.hys.#   匹配
     */
    @Test
    public void sendMessageToMq(){
        AnswerEntity answer=new AnswerEntity();
        answer.setUserId(707L);
        answer.setQuestionId(101L);
        answer.setAnswer("A,B,D");
        answer.setRight("正确-yes");
        rabbitTemplate.convertAndSend("test.topic.exchange", "test.hys.data", answer);
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值