RabbitMQ —— 五、单 Channel 消费之 Disruptor

RabbitMQ —— 五、单 Channel 消费之 Disruptor

       起因不再多说,可看前一篇文章《RabbitMQ —— 四、单 Channel 消费之 ArrayBlockingQueue》,本文是将内部的缓冲队列换成了 Disruptor。

一、核心 Listener

package com.xugy.apprabbit.module.amqp.core;

import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.WorkHandler;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.rabbitmq.client.*;
import com.xugy.apprabbit.boot.autoconfigure.amqp.AmqpProperties;
import com.xugy.apprabbit.module.amqp.AmqpEntry;
import com.xugy.apprabbit.module.amqp.Messagelistener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;

import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @Author xuguangyuansh@126.com
 */
public class AmqpCoreListener {
    protected static final Logger logger = LoggerFactory.getLogger(AmqpCoreListener.class);

    protected final ExecutorService executor = Executors.newCachedThreadPool();

    protected final AmqpProperties amqpProperties;
    protected final Channel channel;
    protected final String consumer;
    protected final String queue;
    protected final int ringBufferSize;
    protected final boolean isAutoAck;
    protected AmqpReceiver amqpReceiver;

    public AmqpCoreListener(Channel channel, String queue, String consumer, AmqpProperties amqpProperties) {
        this.amqpProperties = amqpProperties;
        this.channel = channel;
        this.queue = queue;
        this.consumer = consumer;
        this.isAutoAck = amqpProperties.isAutoAck();

        int ringSize = 1;
        while (ringSize < amqpProperties.getQos()) ringSize = ringSize << 1;
        ringSize = ringSize >> 1;
        this.ringBufferSize = ringSize;
    }

    public AmqpCoreListener start() {
        this.amqpReceiver = new AmqpReceiver();
        return this;
    }

    protected Disruptor disruptorConsumer() {
        Disruptor disruptor1 = disruptorCallback();
        Disruptor disruptor = new Disruptor(() -> new AmqpEntry(), ringBufferSize, Executors.defaultThreadFactory(), ProducerType.SINGLE, new BlockingWaitStrategy());
        disruptor.handleEventsWithWorkerPool(Arrays.stream(new AmqpConsumer[amqpProperties.getCurrent()]).map(x -> new AmqpConsumer(disruptor1)).collect(Collectors.toList()).toArray(new AmqpConsumer[0]));
        disruptor.start();
        return disruptor;
    }

    protected Disruptor disruptorCallback() {
        Disruptor disruptor = new Disruptor(() -> new AmqpEntry(), ringBufferSize, Executors.defaultThreadFactory(), ProducerType.MULTI, new BlockingWaitStrategy());
        disruptor.handleEventsWithWorkerPool(new AmqpCallbacker());
        disruptor.start();
        return disruptor;
    }

    /***********************************************/
    /********            消息生产            ********/
    /***********************************************/
    class AmqpProducer {
        public AmqpProducer() {

        }
    }

    /***********************************************/
    /********            开始接收            ********/
    /***********************************************/
    class AmqpReceiver {
        AmqpReceiver() {
            executor.execute(() -> {
                try {
                    channel.basicQos(amqpProperties.getQos());
                } catch (IOException e) {
                    throw new RuntimeException(">>>>> AmqpCoreListener init: when basicQos Exception: {}", e);
                }
                RingBuffer<AmqpEntry> ringBuffer = disruptorConsumer().getRingBuffer();
                final Consumer consumer = new DefaultConsumer(channel) {
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
                        long sequence = ringBuffer.next();
                        try {
                            AmqpEntry amqpEntry = ringBuffer.get(sequence);
                            amqpEntry.setMessage(new Message(body, null));
                            amqpEntry.setEnvelope(envelope);
                        } finally {
                            ringBuffer.publish(sequence);
                        }
                    }
                };
                try {
                    channel.basicConsume(queue, false, consumer);
                } catch (IOException e) {
                    logger.error(">>>>> AmqpCoreListener basicConsume Exception: ");
                    e.printStackTrace();
                }
            });
        }
    }

    /***********************************************/
    /********            消息消费            ********/
    /***********************************************/
    class AmqpConsumer implements WorkHandler<AmqpEntry> {
        protected final Messagelistener messageListener;
        protected final RingBuffer<AmqpEntry> ringBuffer;

        AmqpConsumer(Disruptor callbacker) {
            this.ringBuffer = callbacker.getRingBuffer();
            try {
                messageListener = (Messagelistener) Class.forName(consumer).getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            messageListener.setCallbackRingBuffer(this.ringBuffer);
        }

        /**
         * Callback to indicate a unit of work needs to be processed.
         *
         * @param amqpEntry published to the {@link RingBuffer}
         */
        @Override
        public void onEvent(AmqpEntry amqpEntry) {
            try {
                messageListener.onMessage(amqpEntry);
                amqpEntry.setAck(Boolean.TRUE);
            } catch (Exception e) {
                amqpEntry.setAck(Boolean.FALSE);
                e.printStackTrace();
            } finally {
                if (isAutoAck) {
                    long sequence = ringBuffer.next();
                    try {
                        AmqpEntry amqpEntry1 = ringBuffer.get(sequence);
                        amqpEntry1.setEnvelope(amqpEntry.getEnvelope());
                        amqpEntry1.setAck(amqpEntry.isAck());
                    } finally {
                        ringBuffer.publish(sequence);
                    }
                }
            }
        }
    }

    /***********************************************/
    /********            消息确认            ********/
    /***********************************************/
    class AmqpCallbacker implements WorkHandler<AmqpEntry> {

        /**
         * Callback to indicate a unit of work needs to be processed.
         *
         * @param amqpEntry published to the {@link RingBuffer}
         */
        @Override
        public void onEvent(AmqpEntry amqpEntry) throws IOException {
            if (amqpEntry.isAck())
                channel.basicAck(amqpEntry.getEnvelope().getDeliveryTag(), false);
            else
                channel.basicNack(amqpEntry.getEnvelope().getDeliveryTag(), false, true);
        }
    }
}

二、一点解析

       这里的等待策略使用了 BlockingWaitStrategy,因经测试,其他几种等待策略,即便是 yield,也会导致 cpu 空转,负载飙升,消息吞吐量会从 1万+ 降到 1千多一点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值