簡體   English   中英

每分鍾發送 35000 條 jms 消息

[英]Send 35000 jms messages per minute

我們有一個 spring 啟動應用程序,用於對另一個組件執行負載測試。 我們每分鍾最多需要發送 35000 條 JMS 消息,因此我使用調度程序每分鍾運行一次任務。

問題是當我保持低強度時,它設法在指定的時間間隔(一分鍾)內發送消息。 但是當強度很高時,發送大量消息需要超過 1 分鍾的時間。 對以下實施有何建議?

調度員 class

@Component
public class MessageScheduler {

private final Logger log = LoggerFactory.getLogger(getClass());
private static ScheduledExecutorService executorService = Executors.newScheduledThreadPool(16);
private final static int TIME_PERIOD = ConfigFactory.getConfig().getInt("messages.period").orElse(60000);

@Autowired
JmsSender sender;

    public void startScheduler() {
       Runnable runnableTask = sender::sendMessagesChunk;
       executorService.scheduleAtFixedRate(runnableTask, 0, TIME_PERIOD, 
       TimeUnit.MILLISECONDS);
    }
}

Class 用於發送消息

@Component
public class JmsSender {

@Autowired
TrackingManager manager;

private final Logger log = LoggerFactory.getLogger(getClass());
private final static int TOTAL_MESSAGES = ConfigFactory.getConfig().getInt("total.tracking.messages").orElse(10);
private final static int TIME_PERIOD = ConfigFactory.getConfig().getInt("messages.period").orElse(60000);
private static int failedPerPeriod=0;
private static int totalFailed=0;
private static int totalMessageCounter=0;

public void sendMessagesChunk() {
    log.info("Started  at: {}", Instant.now());
    log.info("Sending messages with intensity {} messages/minute", TOTAL_MESSAGES);
    for (int i=0; i<TOTAL_MESSAGES; i++) {
        try {
            long start = System.currentTimeMillis();
            MessageDTO msg = manager.createMessage();
            send(msg);
            long stop = System.currentTimeMillis();
            if (timeOfDelay(stop-start)>=0L) {
                Thread.sleep(timeOfDelay(stop-start));
            }
        } catch (Exception e) {
            log.info("Error :  " + e.getMessage());
            failedPerPeriod++;
        }
    }
    totalMessageCounter += TOTAL_MESSAGES;
    totalFailed += failedPerPeriod;
    log.info("Finished  at: {}", Instant.now());
    log.info("Success rate(of last minute): {} %, Succeeded: {}, Failed: {}, Success rate(in total): {} %, Succeeded: {}, Failed: {}"
            ,getSuccessRatePerPeriod(), getSuccededPerPeriod(), failedPerPeriod,
            getTotalSuccessRate(), getTotalSucceded(), totalFailed);
    failedPerPeriod =0;
}

private long timeOfDelay(Long elapsedTime){
    return (TIME_PERIOD / TOTAL_MESSAGES) - elapsedTime;
}
private int getSuccededPerPeriod(){
    return TOTAL_MESSAGES - failedPerPeriod;
}

private int getTotalSucceded(){
    return totalMessageCounter - totalFailed;
}

private double getSuccessRatePerPeriod(){
    return getSuccededPerPeriod()*100D / TOTAL_MESSAGES;
}

private double getTotalSuccessRate(){
    return getTotalSucceded()*100D / totalMessageCounter;
}

private void send(MessageDTO messageDTO) throws Exception {
    requestContextInitializator();
    JmsClient client = JmsClientBuilder.newClient(UriScheme.JmsType.AMQ);
    client.target(new URI("activemq:queue:" + messageDTO.getDestination()))
            .msgTypeVersion(messageDTO.getMsgType(), messageDTO.getVersion())
            .header(Header.MSG_VERSION, messageDTO.getVersion())
            .header(Header.MSG_TYPE, messageDTO.getMsgType())
            .header(Header.TRACKING_ID, UUID.randomUUID().toString())
            .header(Header.CLIENT_ID, "TrackingJmsClient")
            .post(messageDTO.getPayload());
}

你應該解決兩個問題:

  1. 總發送操作時間必須低於最大時間。
  2. 消息應該盡可能快地發送,而應該在所有可用時間內統一發送。

顯然,如果您的send方法太慢,將超過最大時間。

發送消息的更快方法是使用某種批量操作。 沒關系,如果你的MQ API不支持批量操作,你就不能使用它! 因為第二個限制(“統一”)。

您可以異步發送消息,但如果您的MQ API為其創建線程而不是“非阻塞”異步,則可能存在內存問題。

使用javax.jms.MessageProducer.send可以異步發送消息,但是將為每個消息創建一個新的線程(將創建大量內存和服務器線程)。

另一個加速可能只創建一個JMS客戶端(您的send方法)。

要達到第二個要求,你應該修復你的timeOfDelay函數,這是錯誤的。 實際上,您應該考慮send函數的概率分布來估計正確的值,但是,您可以簡單地做:

    long accTime = 0L;
    for (int i=0; i<TOTAL_MESSAGES; i++) {
        try {
            long start = System.currentTimeMillis();
            MessageDTO msg = manager.createMessage();
            send(msg);
            long stop = System.currentTimeMillis();
            accTime += stop - start;
            if(accTime < TIME_PERIOD)
                Thread.sleep((TIME_PERIOD - accTime) / (TOTAL_MESSAGES - i));
        } catch (Exception e) {
            log.info("Error :  " + e.getMessage());
            failedPerPeriod++;
        }
    }

35000 msg/min 低於 600 msg/sec。 這不算“很多”,應該是相對容易清除的目標。 主要想法是“重用”所有重量級 JMS 對象:連接、session 和目的地。 單線程應該足夠了。

ConnectionFactory connFactory = ....    // initialize connection factory
@Cleanup Connection conn = connFactory.createConnection();
@Cleanup Session session = conn.createSession(true, Session.SESSION_TRANSACTED);
Queue q = session.createQueue("example_destiation");
@Cleanup MessageProducer producer = session.createProducer(q);

for (String payload: messagesToSend) {
    TextMessage message = session.createTextMessage(payload);
    producer.send(msg);
    session.commit();
}

額外的加速是可能的:

  • 提交每條第 n 條消息
  • 通過使用更快的確認模式
  • 通過使用非持久消息
  • 通過使用在 session 之外創建的目標 object
  • 異步發送消息

NON_PERSISTENT、ACKOWLEDGE、ASYNC 傳送示例:

@Cleanup Connection conn = connFactory.createConnection();
@Cleanup Session session = conn.createSession(false, Session.DUPS_OK_ACKNOWLEDGE);
Queue q = session.createQueue("example_destiation");
@Cleanup MessageProducer producer = session.createProducer(q);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
producer.setAsync(new ExmpleSendListener());

for (String payload: messagesToSend) {
    TextMessage message = session.createTextMessage(payload);
    producer.send(msg);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM