<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Boot + Disruptor = 王炸!!

          共 11880字,需瀏覽 24分鐘

           ·

          2023-01-06 18:50

          往期熱門文章:

          1、慎用BeanUtils,性能真的拉跨!
          2、千萬不要把 Request 傳遞到異步線程里面!有坑!
          3、新來的CTO規(guī)定所有接口都用POST請求...
          4、Java枚舉意外觸發(fā)了Mybatis Plus的BUG
          5、分庫分表,可能真的要退出歷史舞臺了!

          來源:https://blog.csdn.net/buertianci/article/details/105327031

          01、背景

          工作中遇到項目使用Disruptor做消息隊列,對你沒看錯,不是Kafka,也不是rabbitmq;Disruptor有個最大的優(yōu)點就是快,還有一點它是開源的哦,下面做個簡單的記錄.

          02、Disruptor介紹

          1. Disruptor 是英國外匯交易公司LMAX開發(fā)的一個高性能隊列,研發(fā)的初衷是解決內存隊列的延遲問題(在性能測試中發(fā)現(xiàn)竟然與I/O操作處于同樣的數量級)。基于 Disruptor 開發(fā)的系統(tǒng)單線程能支撐每秒 600 萬訂單,2010 年在 QCon 演講后,獲得了業(yè)界關注。
          2. Disruptor是一個開源的Java框架,它被設計用于在生產者—消費者(producer-consumer problem,簡稱PCP)問題上獲得盡量高的吞吐量(TPS)和盡量低的延遲。
          3. 從功能上來看,Disruptor 是實現(xiàn)了“隊列”的功能,而且是一個有界隊列。那么它的應用場景自然就是“生產者-消費者”模型的應用場合了。
          4. Disruptor是LMAX在線交易平臺的關鍵組成部分,LMAX平臺使用該框架對訂單處理速度能達到600萬TPS,除金融領域之外,其他一般的應用中都可以用到Disruptor,它可以帶來顯著的性能提升。
          5. 其實Disruptor與其說是一個框架,不如說是一種設計思路,這個設計思路對于存在“并發(fā)、緩沖區(qū)、生產者—消費者模型、事務處理”這些元素的程序來說,Disruptor提出了一種大幅提升性能(TPS)的方案。
          6. Disruptor的github主頁:https://github.com/LMAX-Exchange/disruptor

          03、Disruptor 的核心概念

          先從了解 Disruptor 的核心概念開始,來了解它是如何運作的。下面介紹的概念模型,既是領域對象,也是映射到代碼實現(xiàn)上的核心對象。

          04、Ring Buffer

          如其名,環(huán)形的緩沖區(qū)。曾經 RingBuffer 是 Disruptor 中的最主要的對象,但從3.0版本開始,其職責被簡化為僅僅負責對通過 Disruptor 進行交換的數據(事件)進行存儲和更新。在一些更高級的應用場景中,Ring Buffer 可以由用戶的自定義實現(xiàn)來完全替代。

          05、Sequence Disruptor

          通過順序遞增的序號來編號管理通過其進行交換的數據(事件),對數據(事件)的處理過程總是沿著序號逐個遞增處理。一個 Sequence 用于跟蹤標識某個特定的事件處理者( RingBuffer/Consumer )的處理進度。
          雖然一個 AtomicLong 也可以用于標識進度,但定義 Sequence 來負責該問題還有另一個目的,那就是防止不同的 Sequence 之間的CPU緩存?zhèn)喂蚕?Flase Sharing)問題。(注:這是 Disruptor 實現(xiàn)高性能的關鍵點之一,網上關于偽共享問題的介紹已經汗牛充棟,在此不再贅述)。

          06、Sequencer

          Sequencer 是 Disruptor 的真正核心。此接口有兩個實現(xiàn)類 SingleProducerSequencer、MultiProducerSequencer ,它們定義在生產者和消費者之間快速、正確地傳遞數據的并發(fā)算法。

          07、Sequence Barrier

          用于保持對RingBuffer的 main published Sequence 和Consumer依賴的其它Consumer的 Sequence 的引用。Sequence Barrier 還定義了決定 Consumer 是否還有可處理的事件的邏輯。

          08、Wait Strategy

          定義 Consumer 如何進行等待下一個事件的策略。(注:Disruptor 定義了多種不同的策略,針對不同的場景,提供了不一樣的性能表現(xiàn))

          09、Event

          在 Disruptor 的語義中,生產者和消費者之間進行交換的數據被稱為事件(Event)。它不是一個被 Disruptor 定義的特定類型,而是由 Disruptor 的使用者定義并指定。

          10、EventProcessor

          EventProcessor 持有特定消費者(Consumer)的 Sequence,并提供用于調用事件處理實現(xiàn)的事件循環(huán)(Event Loop)。

          11、EventHandler

          Disruptor 定義的事件處理接口,由用戶實現(xiàn),用于處理事件,是 Consumer 的真正實現(xiàn)。

          12、Producer

          即生產者,只是泛指調用 Disruptor 發(fā)布事件的用戶代碼,Disruptor 沒有定義特定接口或類型。

          13、案例-demo

          通過下面8個步驟,你就能將Disruptor Get回家啦:
          1、添加pom.xml依賴
          <dependency>
              <groupId>com.lmax</groupId>
              <artifactId>disruptor</artifactId>
              <version>3.3.4</version>
          </dependency>
          2、消息體Model
          /**
           * 消息體
           */
          @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提供的線程來觸發(fā)consumer的事件處理
                  ExecutorService executor = Executors.newFixedThreadPool(2);

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

                  //指定ringbuffer字節(jié)大小,必須為2的N次方(能將求模運算轉為位運算提高效率),否則將影響效率
                  int bufferSize = 1024 * 256;

                  //單線程模式,獲取額外的性能
                  Disruptor<MessageModel> disruptor = new Disruptor<>(factory, bufferSize, executor,
                          ProducerType.SINGLE, new BlockingWaitStrategy());

                  //設置事件業(yè)務處理器---消費者
                  disruptor.handleEventsWith(new HelloEventHandler());

                  // 啟動disruptor線程
                  disruptor.start();

                  //獲取ringbuffer環(huán),用于接取生產者生產的事件
                  RingBuffer<MessageModel> ringBuffer = disruptor.getRingBuffer();

                  return ringBuffer;
              }
          7、構造Mqservice和實現(xiàn)類-生產者
          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 {
                      //發(fā)布Event,激活觀察者去消費,將sequence傳遞給改消費者
                      //注意最后的publish方法必須放在finally中以確保必須得到調用;如果某個請求的sequence未被提交將會堵塞后續(xù)的發(fā)布操作或者其他的producer
                      messageModelRingBuffer.publish(sequence);
                  }
              }
          }
          8、構造測試類及方法
          Spring Boot 基礎就不介紹了。
          @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("消息隊列已發(fā)送完畢");
                  //這里停止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      : 消息隊列已發(fā)送完畢
          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 是在內存中以隊列的方式去實現(xiàn)的,而且是無鎖的。
          這也是 Disruptor 為什么高效的原因。
          往期熱門文章:

          1、IntelliJ IDEA終于支持對Redis 的可視化窗口操作了,真香!
          2、ChatGPT能接入微信了!
          3、Java 反射慢?它到底慢在哪?
          4、當我去了不到 20 人的 IT 公司后。。。
          5、GitHub 被超火的 ChatGPT 霸榜!
          6、Java使用 try catch會影響性能?
          7、Java使用 try catch會影響性能?
          8、原來count(*)是接口性能差的真兇!
          9、大公司病了,這也太形象了吧!!!
          10、全球最大資源站創(chuàng)始人被抓,但網站還會繼續(xù)活下去


          瀏覽 33
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲欧美一级二级三级 | 欧美成人片网站 | 大香蕉大香蕉大香蕉 | 黄色尤物国产黄色小视频在线观看免费 | 中国特级毛片 |