利用SynchronousQueue多線程處理ActiveMQ消息

當我們想通過多條線程處理activemq中的消息,直覺上會使用固定大小線程池去處理,然而這種方式並不妥當,這麼做我們只是將消息從activemq轉移到線程池的阻塞隊列之中,當線程池開始工作,activemq中的消息快速被消費完畢,而消息所代表的任務卻並未真正被處理, 他們被堆積在處理程序的內存中,並陸續由線程中的線程處理。這會產生副作用,此時當處理程序因爲某種原因而崩潰,這些待處理的任務都將丟失。

如何實現既能通過多個線程處理任務,又能保證未完成的任務的安全性,此時 SynchronousQueue 就有了用武之地。

我們可以把SynchronousQueue 當作長度爲1的阻塞隊列,當隊列被塞入一個元素,假如這個元素未被消費掉,那麼後續的塞入操作將被阻塞。我們可以利用它的這個特性,把它當作是activemq與處理線程之間的緩衝層。在 SynchronousQueue 的一端,我們從activemq中讀取一個元素,並將它put進SynchronousQueue 。在另一端,多條線程分別從 SynchronousQueue 中 take 元素進行處理,只有當 SynchronousQueue 中不存在任何元素,也就是線程們將當前的任務都處理完畢,還有一端的從activemq中提取消息的操作才能執行,反之則將被阻塞。 通過這種方式,我們便能保證任務不丟失的同時又能通過多線程處理它們。示例代碼如下

初始化一個 SynchronousQueue

private SynchronousQueue<ActiveMQObjectMessage> synchronousQueue = new SynchronousQueue<>();

從activemq中將消息轉移至synchronousQueue,一次轉移一條,如果上一條未被處理,下一條不能繼續

ConnectionFactory factory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER, ActiveMQConnection
                    .DEFAULT_PASSWORD, brokerUrl);
connection = factory.createConnection();
connection.start();
session = connection.createSession(Boolean.FALSE, Session.CLIENT_ACKNOWLEDGE);
Destination destination = session.createQueue(dest);
MessageConsumer consumer = session.createConsumer(destination);

while (true) {
  try {
    Message message = consumer.receive();
    if (message instanceof ActiveMQObjectMessage) {
      ActiveMQObjectMessage activeMQObjectMessage = (ActiveMQObjectMessage) message;
      synchronousQueue.put(activeMQObjectMessage);
    } else {
      if (message != null) {
        message.acknowledge();
        logger.error("消息格式錯誤,msg={}",message.toString());
      }
    }
  } catch (JMSException | InterruptedException e) {
    e.printStackTrace();
  }
}

開啓多條線程同時處理消息

  Runnable task = () -> {
            while (true) {
                try {
                    ActiveMQObjectMessage activeMQObjectMessage = synchronousQueue.take();
                      //消費消息,處理成功後確認
                       boolean complete = handle(msg);
                        if (complete) {
                            activeMQObjectMessage.acknowledge();
                        }
                } catch ( JMSException e) {
                    e.printStackTrace();
                }
            }
        };

        for (int i = 0; i < threads; i++) {
            Thread thread = new Thread(task);
            thread.setName("log-task-" + i);
            thread.start();
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章