RabbitMQ基础概念和高级特性

RabbitMQ核心概念

RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理和队列服务器。RabbitMQ服务器是用Erlang语言编写的。

  • Server:也称Broker,接受客户端的连接,实现AMQP实体服务
  • Connection:连接与Broker的网络连接
  • Channel:网络信道,几乎所有的操作都在Channel上进行,一个客户端可以建立多个Channel,每个Channel代表一个会话任务。
  • Message:消息,服务器和应用程序之间传送的数据,由Properties和Body组成。Properties可以对消息进行修饰,比如消息的优先级、延迟等高级特性;Body则是消息内容体。
  • Virtual host:虚拟地址,用于逻辑隔离,最上层的消息路由。
  • Exchange:交换机,接受消息,并根据路由键转发消息所绑定的队列
  • Binding:Exchange和Queue之间的虚拟连接。
  • Routing key:路由规则
  • Queue:消息队列,保存消息并转发给消费者。
Exchange类型
  1. direct – 直连的方式,消息会被转发RouteKey中指定的Queue中
  2. topic – Exchange将RouteKey和Topic进行模糊匹配,此时队列需要绑定一个topic。可以使用通配符继续匹配,“#”匹配一个或者多个词。“*”只能匹配一个词。
  3. fanout – 不处理路由键,只需要简单的将队列绑定到交换机。发送到该交换机的消息都会被转发到该交换机绑定的队列上。Fanout交换机转发消息是最快的。

RabbitMQ一些高级特性

Confirm确认消息:消息的确认,是指生产者投递消息后,如果Broke收到消息会给生产者一个应答

实现流程:

  1. 在Channel上开启确认模式:channel.confirmSelect();
  2. 在Channel上添加监听:channel.addConfirmListener,根据成功和失败结果进行后续的处理。
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.1.221");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        Connection collection = connectionFactory.newConnection();
        Channel channel = collection.createChannel();

        String exchangeName = "test_confirm_exchange";
        String routingKey = "confirm.a";

        //开启消息确认
        channel.confirmSelect();

        channel.addConfirmListener(new ConfirmListener() {
            // 消息成功ack
            @Override
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                System.err.println("-----消息发送成功-----");
            }
            //消息失败ack
            @Override
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                System.err.println("-----消息发送失败-----");
            }
        });

        String msg = "this is a confirm message !!!";
        channel.basicPublish(exchangeName,routingKey,null,msg.getBytes());

Return消息:Return Listener用于处理一些不可路由的消息,exchange或者routingKey找不到。

实现流程:Mandatory:设置为true,则监听器会接收路由不可达的消息。如果为false,那么broker端会自动删除该消息。在Channel上添加addReturnListener监听。

public class ReturnSender {
    public static void main(String[] args) throws IOException, TimeoutException {

        //Connection collection = CollectionUtil.getCollection();
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.1.221");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        Connection collection = connectionFactory.newConnection();
        Channel channel = collection.createChannel();

        String exchangeName = "test_return_exchange";
        String routingKey1 = "return.a";
        String routingKey2 = "abc.return";


        channel.addReturnListener(new ReturnListener() {
            @Override
            public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.err.println("replyCode:"+replyCode);
                System.err.println("replyText:"+replyText);
                System.err.println("exchange:"+exchange);
                System.err.println("routingKey:"+routingKey);
                System.err.println("body:"+new String(body));
            }
        });

        String msg = "this is a confirm message !!!";
        boolean mandatory = true;
        channel.basicPublish(exchangeName,routingKey1,mandatory,null,msg.getBytes());
        channel.basicPublish(exchangeName,routingKey2,mandatory,null,msg.getBytes());
    }
}

消费端限流:rabbitmq提供一种qos(服务质量保证)功能,在非自动确认消息时,如果一定数目的消息未被确认,不进行消费新的消息,会阻塞状态。

实现流程:channel.basicQos(int prefetchCount,boolean global)方法

prefetchCount:一次有prefetchCount没被ack,消费者阻塞,直到有消息ack

消费端ACK和重回队列

消费端可以进行手动ACK和NACK,当NACK时可以让消息重回队列,消息会重新投递到Broker队列尾部。一般关闭,不然容易一直循环。

//生成端代码
public static void main(String[] args) throws IOException, TimeoutException {

        //1、创建ConnectionFactory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.1.221");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        //2、创建连接
        Connection connection = connectionFactory.newConnection();
        //3、创建channel
        Channel channel = connection.createChannel();

        String routingKey = "test001";

        Map<String,Object> headers = new HashMap<>();

        for (int i=0;i<5;i++){
            headers.put("flag",i);
            AMQP.BasicProperties props = new AMQP.BasicProperties().builder()
                    .deliveryMode(2)
                    .contentEncoding("UTF-8")
                    .headers(headers)
                    .build();
            String msg = "this is a test message , number:"+i;
            channel.basicPublish("",routingKey,props,msg.getBytes());
        }

    }
    
    //消费端代码
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //1、创建ConnectionFactory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.1.221");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");

        connectionFactory.setAutomaticRecoveryEnabled(true);
        connectionFactory.setNetworkRecoveryInterval(3000);
        //2、创建连接
        Connection connection = connectionFactory.newConnection();
        //3、创建channel
        Channel channel = connection.createChannel();

        String queueName = "test001";
        //
        channel.queueDeclare(queueName,false,false,false,null);

        QueueingConsumer consumer = new QueueingConsumer(channel);
        //autoAck设置为false
        channel.basicConsume(queueName,false,consumer);
        while (true){
            //获取消息,没有消息会阻塞
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            System.out.println("收到消息:"+new String(delivery.getBody()));
            if ((Integer) delivery.getProperties().getHeaders().get("flag")==0){
                //requeue属性true设置为重回队列,一般设置为false,不然容易一直循环
                channel.basicNack(delivery.getEnvelope().getDeliveryTag(),false,true);
            }else {
                channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
            }

        }

    }

死信队列:DLX,Dead-Letter-Exchange。利用DLX,一个消息变为死信后,会被重新publish到另一个Exchange,这个Exchange就是DLX。

消息变为死信的几种情况

  1. 消息被拒绝,并且requeue=false
  2. 消息TTL过期
  3. 队列达到最大长度

DLX也是一个普通的Exchange,只是设置了队列属性(添加x-dead-letter-exchange),当这个队列有死信时,RabbitMQ就会自动将这个消息发布到设置的Exchange上,进而被路由到另一个队列。

消息如何保障100%的投递成功?

什么是生产端的可靠性投递

  • 保障消息成功发出
  • 保障MQ节点的成功接收
  • 发送端收到MQ节点的确认应答(ack)
  • 完善的消息补偿机制

解决方案:消息落库,对消息状态进行打标

消费端-幂等性保障

主流的幂等性操作:业务唯一ID(或)指纹码机制,利用数据库主键去重

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值