为JMS消息创建分发ParallelStream处理

时间:2016-03-11 01:30:21

标签: java activemq apache-kafka

我正在尝试扩展以下示例程序,该程序创建一个简单的JMS消息并同时将其推送到同一队列。目前,示例程序在单个四核主机上大约需要20秒。任何人都可以建议对以下内容进行一些更改以提高性能吗? 20秒测量仅适用于以下并行流处理线:

test.parallelStream().forEach(e -> sender.sendMessage(e));

我能想到的一种方法是将我的Collection(“test”)展开给几个主机,然后通过将每个线程配置为拥有自己的线程池,然后以块的形式同时处理集合。这样做的一个缺点是容错,并且必须放置适当的构造以确保每个线程池不处理相同的消息。

另一种方法是使用更高性能/并发的代理,如Kafka

请注意无论我采用什么异步线程方法,我都需要能够控制从这些线程向另一个应用程序发送的消息数量,因为它们只支持一定数量的并发消息。还有其他想法吗?

完整来源:

import java.util.ArrayList;
import java.util.List;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class Sender {
    private static ConnectionFactory factory = null;
    private static Connection connection = null;
    private static Session session = null;
    private static Destination destination = null;
    private static MessageProducer producer = null;

    public Sender() {}

    public void sendMessage(String test) {
        try {
            TextMessage message = session.createTextMessage();
            message.setText(test);
            producer.send(message);
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws JMSException {
            factory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_BROKER_URL);
    ((ActiveMQConnectionFactory)factory).setUseAsyncSend(true);
    ((ActiveMQConnectionFactory)factory).setOptimizeAcknowledge(true);
    connection = factory.createConnection();
    connection.start();
    session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
    destination = session.createQueue("SAMPLEQUEUE");
    producer = session.createProducer(destination);
    producer.setDeliveryMode(DeliveryMode.PERSISTENT);

        List<String> test = new ArrayList<String>();
        for (int i = 0; i <= 100000; i++) {
            test.add(Integer.toString(i));
        }
        Sender sender = new Sender();
        test.parallelStream().forEach(e -> sender.sendMessage(e));
    }
}

2 个答案:

答案 0 :(得分:1)

正如所指出的,MessagProducer不保证是线程安全的,但也不是Session。

无论如何,关于这一点:

  1. 我强烈建议不要使用默认流api进行IO操作,因为它使用一个线程池进行所有操作,并且线程池限制为内核数量加上你不能指定超时。您应该使用Executor:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newCachedThreadPool--
  2. 
        ExecutorService executor = Executors.newFixedThreadPool(2);
        for (String msg: msgs) {
            executor.execute(() -> send(msg)); 
        }
    
    
    1. 如果您不必保证邮件传递,您可以将邮件传递模式更改为NON_PERSISTENT,这可以加快发送速度,因为邮件存储没有任何开销并且保证交付。
    2. 实际上,虽然您不需要自己进行线程管理,因为ActiveMQ具有异步支持,这意味着只要您不必保证传递,就可以在单独的线程上确认您的消息:http://activemq.apache.org/async-sends.html
    3. 如果您确实需要保证交付,那么请在JMS事务中使用PERSISTENT消息传递模式(默认)来批量发送您的消息(一种全有或全无的方法)。这提供了很大的性能提升,因为您只在提交期间进行实际发送。 https://docs.oracle.com/javaee/6/api/javax/jms/Connection.html#createSession(boolean, int)
    4. 在ActiveMQ中启用optimizeAcknowledge以加速消息代理本身:http://activemq.apache.org/optimized-acknowledgement.html
    5. 最后,您可以将会话确认模式设置为Session.DUPS_OK_ACKNOWLEDGE,以便在后台延迟确认消息,但这会产生重复的消息发送,因此消费者方需要使用唯一ID或类似内容为此做好准备
    6. 当然,你不应该一起使用所有这些方法,只需使用常识来适应另一种方法。

答案 1 :(得分:1)

除了上面所说的内容之外,我还将专注于您如何使用消息。如果您的接收端应用程序可以同时处理有限数量的消息,那么您同时发送的消息数量并不重要,因为它们将在队列中等待,直到您的接收应用程序准备好消耗它们。所以,说你的接收应用程序可以同时处理10个请求。我会在你的队列中设置10个消费者,每个消费者只有在处理了之前处理过的请求之后才从队列中读取传入的请求。这样,传入消息的速度并不重要(除了担心队列溢出)。而且您的应用程序将始终同时处理不超过10个请求。