RabbitMQ

消息队列:是在消息传输过程中,保存消息的容器,用于分布式系统之间通信
在这里插入图片描述
在这里插入图片描述rabbitmq就是erlang语言编写实现AMPQ协议的消息中间件
AMPQ协议:
connection:连接
channel:轻量级的connection
exchange:只控制消息的去向,交换机,支持通配符:*匹配一个单词,#匹配多个单词
queue:存储消息的容器
为什么要使用RabbitMQ?
1.解耦:a服务本身调用b,c服务。如果需要添加d服务则不需要修改a服务的源代码
2.异步:a服务把消息发送到mq,b,c服务接受后通过异步的方式处理
2.削峰:按照并发的能力拉取消息,慢慢处理消息

五种消息模型

1.简单模型
在这里插入图片描述生产者将消息发送到队列,消费者从队列中获取消息
在使用的时候生产者需要声明队列
生产者:获取连接,获取通道,通过通道声明队列,声明消息

public class Send {

    private final static String QUEUE_NAME = "simple_queue";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        // 轻量级的 Connection,这是完成大部分API的地方。
        Channel channel = connection.createChannel();

        // 声明(创建)队列,必须声明队列才能够发送消息,我们可以把消息发送到队列中。
        // 声明一个队列是幂等的 - 只有当它不存在时才会被创建
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        // 消息内容
        String message = "Hello World!";
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");

        //关闭通道和连接
        channel.close();
        connection.close();
    }
}

消费者:1.获取连接,2.创建通道,3.通过通道声明队列,4.定义队列的消费者,5.进行ack的确认

 public static void main(String[] argv) throws Exception {
        // 获取到连接
        Connection connection = ConnectionUtil.getConnection();
        // 创建通道
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 定义队列的消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            // 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, 
                          BasicProperties properties,byte[] body) throws IOException {
                // body 即消息体
                String msg = new String(body);
                System.out.println(" [x] received : " + msg + "!");
            }
        };
        // 监听队列,第二个参数:是否自动进行消息确认。
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }

消息确认机制(ACK)
如果消息一旦被消费者接受,队列中的消息就会被删除,那为什么会被删除
因为RabbitMq有一种消息确认机制,当消费者获取消息后,会向RabbitMq发送回执ACK,告知消息已经被接受
(1)自动ACK:消息一旦被接受,消费者自动发送ACK
(2)手动ACK:接收消息后,不会发送ACK,需要手动
当我们接受消息后,处理业务发生错误异常的时候,这时如果采用自动ACK,我们将无法再获取该消息进行此业务的再次处理,这时候需要使用手动ack,就算我们业务出现错误,再接受消息后不自动发送ack,什么时候处理正常,则手动发送ack,

更改为手动ACK的代码

public class Recv2 {
    private final static String QUEUE_NAME = "simple_queue";

    public static void main(String[] argv) throws Exception {
        // 获取到连接
        Connection connection = ConnectionUtil.getConnection();
        // 创建通道
        final Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 定义队列的消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            // 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, 
                          BasicProperties properties,byte[] body) throws IOException {
                // body 即消息体
                String msg = new String(body);
                System.out.println(" [x] received : " + msg + "!");
                /**
                 * 手动进行ACK
                 *  deliveryTag:该消息的index
                 *  multiple:是否批量处理.true:将一次性ack所有消息。
                 */
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        // 监听队列,第二个参数false,手动进行ACK
        channel.basicConsume(QUEUE_NAME, false, consumer);
    }
}

工作模型
在这里插入图片描述
一个生产者对多个消费者,但是一个消息只能被一个消费者获取
避免消息堆积:采用工作队列,使用多个消费者,消费者采用能者多劳的方式
广播模型
在这里插入图片描述
生产者:声明交换机,声明消息
消费者:声明队列,并且绑定交换机,同时声明队列消费者
类似订阅公众号:公众号发消息,所有订阅该号的用户都能收到消息
定向模型
在这里插入图片描述
生产者声明交换机的时候定义了routing key ,消费者声明队列绑定到交换机同时指定routing key
主题模型
在这里插入图片描述
整体与定向模式,区别就是在于routing key加入通配符规则,

Spring AMQP

Spring-amqp是对AMQP协议的抽象实现,而spring-rabbit 是对协议的具体实现,也是目前的唯一实现。底层使用的就是RabbitMQ。
引入依赖

 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

application.properties中添加RabbitMQ地址:

#主机
spring.rabbitmq.host=192.168.233.132
#端口
spring.rabbitmq.port=5672
#用户名
spring.rabbitmq.username=admin
#密码
spring.rabbitmq.password=1111
#虚拟分组
spring.rabbitmq.virtual-host=/

监听者
在SpringAmqp中,对消息的消费者进行了封装和抽象,一个普通的JavaBean中的普通方法,只要通过简单的注解,就可以成为一个消费者。

@Component
public class Listener {

    /**
     * 监听者接收消息三要素:
     *  1、queue
     *  2、exchange
     *  3、routing key
     */
    @RabbitListener(bindings = @QueueBinding(
        value = @Queue(value="springboot_queue",durable = "true"),
        exchange = @Exchange(value="springboot_exchage",type= ExchangeTypes.TOPIC),
        key= {"*.*"}
    ))
    public void listen(String msg){
        System.out.println("接收到消息:" + msg);
    }
}

监听三要素
队列,交换机,绑定交换机队列的routing key

  • @Componet:类上的注解,注册到Spring容器
  • @RabbitListener:方法上的注解,声明这个方法是一个消费者方法,需要指定下面的属性:
    • bindings:指定绑定关系,可以有多个。值是@QueueBinding的数组。@QueueBinding包含下面属性:
      • value:这个消费者关联的队列。值是@Queue,代表一个队列
      • exchange:队列所绑定的交换机,值是@Exchange类型
      • key:队列和交换机绑定的RoutingKey

类似listen这样的方法在一个类中可以写多个,就代表多个消费者。

AmqpTemplate
Spring为AMQP提供了统一的消息处理模板:AmqpTemplate,非常方便的发送消息,其发送方法:
在这里插入图片描述- 指定交换机、RoutingKey和消息体

  • 指定消息
  • 指定RoutingKey和消息,会向默认的交换机发送消息

手动ACK

#设置三种订阅模式手动ack
spring.rabbitmq.listener.direct.acknowledge-mode=manual
#设置work消息类型手动ack
spring.rabbitmq.listener.simple.acknowledge-mode=manual
@Component
public class Recv {
    /**
     * 监听者接收消息三要素:
     *  1、queue
     *  2、exchange
     *  3、routing key
     */
    @RabbitListener(bindings = @QueueBinding(
        value = @Queue(value="springboot_queue",durable = "true"),
        exchange = @Exchange(value="springboot_exchage",type= ExchangeTypes.TOPIC),
        key= {"*.*"}
    ))
    public void listen(String msg, Channel channel, Message message) throws IOException {
        System.out.println("接收到消息:" + msg);
        //int a = 6/0;
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
}

监听方法添加Channel channel, Message message两个参数

持久化

1、步骤
    1、queue(队列)
         channel.queueDeclare(,true, , , );
    2、exchange(交换机)
         channel.exchangeDeclare( , , true);
    3、message(消息)
         channel.basicPublish(, , MessageProperties.PERSISTENT_TEXT_PLAIN,);

2、问题:如何保证消息成功消费?
    1、rabbitmq会宕机?
        持久化
    2、consumer业务会处理失败?
        手动ack
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值