Spring Boot + Disruptor реализует очереди сообщений и рассказывает вам, что такое быстрота и эффективность!

01. Фон

На работе я столкнулся с проектом, который использовал Disruptor в качестве очереди сообщений. Вы правильно прочитали, это не Kafka и не RabbitMQ; Самое большое преимущество Disruptor в том, что он быстрый и имеет открытый исходный код. Давайте сделаем простую запись ниже.

02. Введение в Disruptor

  1. Disruptor — это высокопроизводительная очередь, разработанная LMAX, британской компанией по торговле иностранной валютой. Первоначальная цель исследований и разработок заключалась в решении проблемы задержки очередей памяти (в тестах производительности было обнаружено, что она находится в том же порядке, что и величину как операции ввода-вывода). Система, разработанная на основе Disruptor, способна поддерживать 6 миллионов заказов в секунду в одном потоке.После выступления на QCon в 2010 году она привлекла внимание отрасли.
  2. Disruptor — это платформа Java с открытым исходным кодом, предназначенная для достижения максимально возможной пропускной способности (TPS) и минимально возможной задержки при решении проблемы производитель-потребитель (PCP).
  3. С функциональной точки зрения Disruptor реализует функцию «очереди», и это ограниченная очередь. Тогда сценарий его применения, естественно, является сценарием применения модели «производитель-потребитель».
  4. Disruptor является ключевым компонентом онлайн-торговой платформы LMAX. Платформа LMAX использует эту структуру для обработки заказов со скоростью 6 миллионов TPS. Помимо финансовой сферы, Disruptor может использоваться в других общих приложениях. Он может принести значительную выгоду Улучшения производительности.
  5. На самом деле Disruptor — это не столько фреймворк, сколько идея дизайна. Для программ с такими элементами, как «параллелизм, буферы, модели производитель-потребитель и обработка транзакций», Disruptor предлагает крупномасштабную идею дизайна. Решения для повышения производительности ( ТПС).
  6. Домашняя страница Disruptor на github: https://github.com/LMAX-Exchange/disruptor.

03. Основная концепция Disruptor

Давайте начнем с понимания основных концепций Disruptor, чтобы понять, как он работает. Представленная ниже концептуальная модель является одновременно объектом предметной области и основным объектом, сопоставленным с реализацией кода.

04、Кольцевой буфер

Как следует из названия, это кольцевой буфер. RingBuffer раньше был основным объектом в Disruptor, но начиная с версии 3.0 его обязанности были упрощены и теперь он отвечает только за хранение и обновление данных (событий), которыми обмениваются через Disruptor. В некоторых более сложных сценариях приложений кольцевой буфер можно полностью заменить пользовательской реализацией.

05、Разрушитель последовательности

Обмениваемые через него данные (события) нумеруются и управляются посредством последовательного возрастания порядковых номеров, причем обработка данных (событий) всегда производится инкрементально по порядковому номеру. Последовательность используется для отслеживания хода обработки определенного обработчика событий (RingBuffer/Consumer).

Хотя AtomicLong также можно использовать для определения прогресса, определение последовательности, ответственной за эту проблему, имеет еще одну цель, а именно предотвратить проблему ложного совместного использования кэша ЦП (Flase Sharing) между различными последовательностями. (Примечание: это один из ключевых моментов Disruptor для достижения высокой производительности. В Интернете уже есть много описаний проблемы псевдосовместного использования, поэтому я не буду здесь вдаваться в подробности).

06、Секвенсор

Sequencer — это настоящее ядро ​​Disruptor. Этот интерфейс имеет два класса реализации: SingleProducerSequencer и MultiProducerSequencer, которые определяют алгоритмы параллелизма для быстрой и правильной передачи данных между производителями и потребителями.

07、Барьер последовательности

Используется для хранения ссылок на основную опубликованную последовательность RingBuffer и последовательности других потребителей, от которых зависит потребитель. Барьер последовательности также определяет логику, которая определяет, есть ли у потребителя события, которые можно обработать.

08、Стратегия ожидания

Определите стратегию того, как Потребитель ждет следующего события. (Примечание: Disruptor определяет множество различных стратегий, обеспечивающих разную производительность для разных сценариев)

09、Событие

В семантике Disruptor данные, которыми обмениваются производители и потребители, называются событиями. Это не конкретный тип, определенный Disruptor, а определяемый и указанный пользователем Disruptor.

10、Процессор событий

EventProcessor хранит последовательность конкретного потребителя (Consumer) и предоставляет цикл событий (Event Loop) для вызова реализации обработки событий.

11、Обработчик событий

Интерфейс обработки событий, определенный Disruptor, реализуется пользователем и используется для обработки событий. Это реальная реализация Consumer.

12, Продюсер

То есть производитель обычно ссылается на пользовательский код, который вызывает Disruptor для публикации событий. Disruptor не определяет конкретный интерфейс или тип.

13. Кейс-демо

Порекомендуйте базовое руководство по Spring Boot и практические примеры: https://github.com/javastacks/spring-boot-best-practice .

Выполнив следующие 8 шагов, вы можете получить Disruptor Get home:

1. Добавьте зависимость pom.xml

<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.4</version>
</dependency>

2. Модель тела сообщения

/**
 * 消息体
 */
@Data
public class MessageModel {
    private String message;
}

3. Создайте EventFactory

public class HelloEventFactory implements EventFactory<MessageModel> {
    @Override
    public MessageModel newInstance() {
        return new MessageModel();
    }
}

4. Создайте EventHandler-потребитель

@Slf4j
public class HelloEventHandler implements EventHandler<MessageModel> {
    @Override
    public void onEvent(MessageModel event, long sequence, boolean endOfBatch) {
        try {
            //这里停止1000ms是为了确定消费消息是异步的
            Thread.sleep(1000);
            log.info("消费者处理消息开始");
            if (event != null) {
                log.info("消费者消费的信息是:{}",event);
            }
        } catch (Exception e) {
            log.info("消费者处理消息失败");
        }
        log.info("消费者处理消息结束");
    }
}

5. Создайте BeanManager

/**
 * 获取实例化对象
 */
@Component
public class BeanManager implements ApplicationContextAware {

    private static ApplicationContext applicationContext = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() { return applicationContext; }

    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }
}

6. Создайте MQManager

@Configuration
public class MQManager {

    @Bean("messageModel")
    public RingBuffer<MessageModel> messageModelRingBuffer() {
        //定义用于事件处理的线程池, Disruptor通过java.util.concurrent.ExecutorSerivce提供的线程来触发consumer的事件处理
        ExecutorService executor = Executors.newFixedThreadPool(2);

        //指定事件工厂
        HelloEventFactory factory = new HelloEventFactory();

        //指定ringbuffer字节大小,必须为2的N次方(能将求模运算转为位运算提高效率),否则将影响效率
        int bufferSize = 1024 * 256;

        //单线程模式,获取额外的性能
        Disruptor<MessageModel> disruptor = new Disruptor<>(factory, bufferSize, executor,
                ProducerType.SINGLE, new BlockingWaitStrategy());

        //设置事件业务处理器---消费者
        disruptor.handleEventsWith(new HelloEventHandler());

        // 启动disruptor线程
        disruptor.start();

        //获取ringbuffer环,用于接取生产者生产的事件
        RingBuffer<MessageModel> ringBuffer = disruptor.getRingBuffer();

        return ringBuffer;
    }

7. Создайте Mqservice и создайте класс-производитель реализации.

public interface DisruptorMqService {

    /**
     * 消息
     * @param message
     */
    void sayHelloMq(String message);
}

@Slf4j
@Component
@Service
public class DisruptorMqServiceImpl implements DisruptorMqService {

    @Autowired
    private RingBuffer<MessageModel> messageModelRingBuffer;

    @Override
    public void sayHelloMq(String message) {
        log.info("record the message: {}",message);
        //获取下一个Event槽的下标
        long sequence = messageModelRingBuffer.next();
        try {
            //给Event填充数据
            MessageModel event = messageModelRingBuffer.get(sequence);
            event.setMessage(message);
            log.info("往消息队列中添加消息:{}", event);
        } catch (Exception e) {
            log.error("failed to add event to messageModelRingBuffer for : e = {},{}",e,e.getMessage());
        } finally {
            //发布Event,激活观察者去消费,将sequence传递给改消费者
            //注意最后的publish方法必须放在finally中以确保必须得到调用;如果某个请求的sequence未被提交将会堵塞后续的发布操作或者其他的producer
            messageModelRingBuffer.publish(sequence);
        }
    }
}

8. Создание тестовых классов и методов

Я не буду рассказывать об основах Spring Boot, рекомендую это практическое руководство: https://github.com/javastacks/spring-boot-best-practice

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class DemoApplicationTests {

    @Autowired
    private DisruptorMqService disruptorMqService;
    /**
     * 项目内部使用Disruptor做消息队列
     * @throws Exception
     */
    @Test
    public void sayHelloMqTest() throws Exception{
        disruptorMqService.sayHelloMq("消息到了,Hello world!");
        log.info("消息队列已发送完毕");
        //这里停止2000ms是为了确定是处理消息是异步的
        Thread.sleep(2000);
    }
}

Результаты тестового запуска

2020-04-05 14:31:18.543  INFO 7274 --- [           main] c.e.u.d.d.s.Impl.DisruptorMqServiceImpl  : record the message: 消息到了,Hello world!
2020-04-05 14:31:18.545  INFO 7274 --- [           main] c.e.u.d.d.s.Impl.DisruptorMqServiceImpl  : 往消息队列中添加消息:MessageModel(message=消息到了,Hello world!)
2020-04-05 14:31:18.545  INFO 7274 --- [           main] c.e.utils.demo.DemoApplicationTests      : 消息队列已发送完毕
2020-04-05 14:31:19.547  INFO 7274 --- [pool-1-thread-1] c.e.u.d.disrupMq.mq.HelloEventHandler    : 消费者处理消息开始
2020-04-05 14:31:19.547  INFO 7274 --- [pool-1-thread-1] c.e.u.d.disrupMq.mq.HelloEventHandler    : 消费者消费的信息是:MessageModel(message=消息到了,Hello world!)
2020-04-05 14:31:19.547  INFO 7274 --- [pool-1-thread-1] c.e.u.d.disrupMq.mq.HelloEventHandler    : 消费者处理消息结束

14. Резюме

На самом деле модель генератор->потребитель очень распространена, и вышеупомянутых эффектов можно легко достичь с помощью некоторых очередей сообщений. Разница в том, что Disruptor реализован как очередь в памяти и не имеет блокировок.

Вот почему Disruptor эффективен.

Заявление об авторских правах: эта статья является оригинальной статьей блоггера CSDN "Fuer Tianci" и соответствует соглашению об авторских правах CC 4.0 BY-SA. При перепечатке прикрепите ссылку на первоисточник и это заявление. Исходная ссылка: https://blog.csdn.net/buertianci/article/details/105327031 .

Рекомендуемые последние популярные статьи:

1. Сборник более 1000 вопросов и ответов на собеседовании по Java (последняя версия 2022 г.).

2. Взрывоопасно! Сопрограммы Java уже на подходе. . .

3. Учебник по Spring Boot 2.x, полный!

4. Перестаньте заполнять экран взрывоопасными категориями и попробуйте режим декоратора. Это элегантный способ! !

5. Недавно выпущено «Руководство по разработке Java (Songshan Edition)», скачайте его скорее!

Если вы считаете, что это хорошо, не забудьте поставить лайк и сделать репост!

おすすめ

転載: blog.csdn.net/youanyyou/article/details/133032721