Loading

RabbitMQ:消费端限流&&消费端ACK与重回队列

一.消费端限流场景

如果RabbitMQ服务上堆积了成千上万条未处理的消息,然后随便打开一个消费者客户端,巨量的消息瞬间被推送过来,但是单个客户端无法同时处理这么多消息,可能会导致服务器宕机,产生线上故障。

所以RabbitMQ提供了一种qos功能(服务质量保证),即在非自动确认消息的前提下,如果一定数目的消息(通过基于consume或者channel设置Qos的值)未被确认前,不进行消费新的消息。

二.BasicQos方法

void BasicQos(int prefetchSize,int prefetchCount,boolean global)

prefetchSize:消费端一般设置为0

prefetchCount:消费者同时接收消息的个数

global:true/false  是否将上面的设置应用于channel级别(是channel级别还是consumer级别)

prefetchCount和global这两项,rabbitmq没有实现,即在自动应答情况下这两个值是不生效的。

消费端

public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //创建一个连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.10.132");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //通过连接创建一个Channel
        Channel channel = connection.createChannel();
        //创建一个队列
        String queueName = "qos";
        channel.queueDeclare(queueName,true,false,false,null);
        //限流策略,第一件事要设置autoAck为false,即下面basicConsume方法的第二个参数
        channel.basicQos(0,1,false);
        //设置Channel
        channel.basicConsume(queueName,false,new MyConsumer(channel));

    }
}

自定义consumer:
public class MyConsumer extends DefaultConsumer {

    private Channel channel;

    public MyConsumer(Channel channel) {
        super(channel);
        this.channel  = channel;
    }

    @Override
    public void handleDelivery(String consumerTag,
                               Envelope envelope,
                               AMQP.BasicProperties properties,
                               byte[] body) throws IOException {
        System.out.println(consumerTag);
        System.out.println(envelope);
        System.out.println(properties);
        System.out.println(new String(body));
        channel.basicAck(envelope.getDeliveryTag(),false);
    }
}

二.消费端ACK与重回队列

消费端ACK使用场景:
1.消费端进行消费的时候,如果由于业务异常我们可以进行日志记录,然后进行补偿。
2.由于服务器宕机等严重问题,那我们就需要手工进行ACK保障消费端消费成功。

生产端:

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建一个连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.10.132");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //通过连接创建一个Channel
        Channel channel = connection.createChannel();

        //通过Channel发送数据
        // 在这里要设置Mandatory(第三个参数)为true,否则broker会自动删除消息
        for(int i=0;i<10;i++){
            Map<String ,Object> hearders = new HashMap<>();
            hearders.put("ack","ok"+i);
            AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                    .deliveryMode(2)
                    .contentEncoding("UTF-8")
                    .headers(hearders)
                    .build();
            channel.basicPublish("","ack",properties,"hello world".getBytes());
        }
        channel.close();
        connection.close();
    }

 消费端:

public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //创建一个连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.10.132");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //通过连接创建一个Channel
        Channel channel = connection.createChannel();
        //创建一个队列
        String queueName = "ack";
        channel.queueDeclare(queueName,true,false,false,null);
        //设置Channel
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body));
                Object ack = properties.getHeaders().get("ack");
                System.out.println(ack.toString());
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if("ok0".equals(ack.toString())){
                    //表示消息处理失败了,设置重回队列,Broker端就会将没有成功处理的消息重新发送,并且位于队列底端。
                    //参数3:requeue 是否重回队列(实际生产会设置false)
                    channel.basicNack(envelope.getDeliveryTag(),false,true);
                }else{
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        //手工签收,必须关闭autoAck(false)
        channel.basicConsume(queueName,false,consumer);

    }

  打印结果:

posted @ 2020-06-02 13:08  秋风飒飒吹  阅读(423)  评论(0编辑  收藏  举报