目前,我们在ActiveMQ库之上使用一些自定义代码进行JMS消息传递。我一直在寻找切换到Camel,易于使用,易于维护和可靠性。
使用我目前的配置,Camel的ActiveMQ实现比我们的旧实现慢得多,无论是发送和接收的每条消息的延迟,还是发送和接收大量消息所花费的时间。我试过调整一些配置(例如最大连接数),但没有用。
我有两个应用程序,一个使用我们的旧实现,一个使用Camel实现。每个应用程序都将JMS消息发送到本地ActiveMQ服务器上的主题,并且还侦听有关该主题的消息。这用于测试两个场景: - 在循环中向主题发送100,000条消息,并查看从开始发送到结束处理所有这些消息所需的时间。 - 每100毫秒发送一条消息,并测量从发送到处理每条消息的延迟(以ns为单位)。
我可以根据为消息泛滥和个别消息处理的时间而改进下面的实现吗?理想情况下,改进将涉及调整我错过的一些配置,或建议更好的方法来做,而不是太hacky。对改进的解释将不胜感激。
编辑:现在,我正在异步发送邮件,我似乎有并发问题。 receivedCount
未达到100,000。查看ActiveMQ Web界面,排队100,000条消息,排队100,000条,因此消息处理方面可能存在问题。我已将receivedCount
更改为AtomicInteger
并添加了一些日志记录以帮助调试。这可能是Camel本身(或ActiveMQ组件)的问题,还是消息处理代码有问题?据我所知,只有~99,876条消息正在传递给floodProcessor.process
。
编辑:针对并发问题更新了异步发送和日志记录。
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.camel.component.ActiveMQComponent;
import org.apache.activemq.pool.PooledConnectionFactory;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.jms.JmsConfiguration;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.log4j.Logger;
public class CamelJmsTest{
private static final Logger logger = Logger.getLogger(CamelJmsTest.class);
private static final boolean flood = true;
private static final int NUM_MESSAGES = 100000;
private final CamelContext context;
private final ProducerTemplate producerTemplate;
private long timeSent = 0;
private final AtomicInteger sendCount = new AtomicInteger(0);
private final AtomicInteger receivedCount = new AtomicInteger(0);
public CamelJmsTest() throws Exception {
context = new DefaultCamelContext();
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(connectionFactory);
JmsConfiguration jmsConfiguration = new JmsConfiguration(pooledConnectionFactory);
logger.info(jmsConfiguration.isTransacted());
ActiveMQComponent activeMQComponent = ActiveMQComponent.activeMQComponent();
activeMQComponent.setConfiguration(jmsConfiguration);
context.addComponent("activemq", activeMQComponent);
RouteBuilder builder = new RouteBuilder() {
@Override
public void configure() {
Processor floodProcessor = new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
int newCount = receivedCount.incrementAndGet();
//TODO: Why doesn't newCount hit 100,000? Remove this logging once fixed
logger.info(newCount + ":" + exchange.getIn().getBody());
if(newCount == NUM_MESSAGES){
logger.info("all messages received at " + System.currentTimeMillis());
}
}
};
Processor spamProcessor = new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
long delay = System.nanoTime() - timeSent;
logger.info("Message received: " + exchange.getIn().getBody(List.class) + " delay: " + delay);
}
};
from("activemq:topic:test?exchangePattern=InOnly")//.threads(8) // Having 8 threads processing appears to make things marginally worse
.choice()
.when(body().isInstanceOf(List.class)).process(flood ? floodProcessor : spamProcessor)
.otherwise().process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
logger.info("Unknown message type received: " + exchange.getIn().getBody());
}
});
}
};
context.addRoutes(builder);
producerTemplate = context.createProducerTemplate();
// For some reason, producerTemplate.asyncSendBody requires an Endpoint to be passed in, so the below is redundant:
// producerTemplate.setDefaultEndpointUri("activemq:topic:test?exchangePattern=InOnly");
}
public void send(){
int newCount = sendCount.incrementAndGet();
producerTemplate.asyncSendBody("activemq:topic:test?exchangePattern=InOnly", Arrays.asList(newCount));
}
public void spam(){
Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
timeSent = System.nanoTime();
send();
}
}, 1000, 100, TimeUnit.MILLISECONDS);
}
public void flood(){
logger.info("starting flood at " + System.currentTimeMillis());
for (int i = 0; i < NUM_MESSAGES; i++) {
send();
}
logger.info("flooded at " + System.currentTimeMillis());
}
public static void main(String... args) throws Exception {
CamelJmsTest camelJmsTest = new CamelJmsTest();
camelJmsTest.context.start();
if(flood){
camelJmsTest.flood();
}else{
camelJmsTest.spam();
}
}
}
答案 0 :(得分:4)
从您当前的JmsConfiguration
看来,您只使用单个线程消费消息。是这个意图吗?
如果没有,则需要将concurrentConsumers
属性设置为更高的值。这将创建一个JMS侦听器的线程池来为您的目标服务。
示例:
JmsConfiguration config = new JmsConfiguration(pooledConnectionFactory);
config.setConcurrentConsumers(10);
这将创建10个JMS侦听器线程,它们将从队列中同时处理消息。
修改强>
对于主题,您可以执行以下操作:
JmsConfiguration config = new JmsConfiguration(pooledConnectionFactory);
config.setConcurrentConsumers(1);
config.setMaxConcurrentConsumers(1);
然后在你的路线中:
from("activemq:topic:test?exchangePattern=InOnly").threads(10)
此外,在ActiveMQ中,您可以使用virtual destination。虚拟主题将像队列一样运行,然后您可以使用与普通队列相同的concurrentConsumers方法。
进一步编辑(发送):
您正在进行阻止发送。您需要producerTemplate.asyncSendBody()
。
修改强>
我刚用你的代码构建了一个项目并运行它。我在floodProcessor
方法中设置了一个断点,newCount
达到了100,000。我想你可能会被你的日志记录以及你异步发送和接收的事实所抛弃。在我的机器上,newCount达到100,000并且"all messages recieved"
消息在执行后1秒内被记录,但程序继续记录了45秒,因为它被缓冲了。您可以通过减少日志记录来查看记录newCount
号与身体号码的接近程度的效果。我将日志记录转为info
,关闭了驼峰记录,并且在记录结束时匹配了两个数字:
INFO CamelJmsTest - 99996:[99996]
INFO CamelJmsTest - 99997:[99997]
INFO CamelJmsTest - 99998:[99998]
INFO CamelJmsTest - 99999:[99999]
INFO CamelJmsTest - 100000:[100000]
INFO CamelJmsTest - all messages received at 1358778578422
答案 1 :(得分:2)
我从原始海报中接手将此作为另一项任务的一部分,并发现丢失消息的问题实际上是在ActiveMQ配置中。
我们设置了sendFailIfNoSpace = true,如果我们发送足够快的邮件来填充发布者缓存,则导致邮件被删除。使用policyEntry主题缓存大小我可以改变消失的消息数量,其可靠性与对这种竞争条件的预期一致。设置sendFailIfNoSpace = false(默认值),我可以拥有自己喜欢的任何缓存大小,并且永远不会收到所有邮件。
理论上,sendFailIfNoSpace在丢弃消息时应该抛出ResourceAllocationException,但这不是发生(!)或以某种方式被忽略。同样有趣的是,尽管运行吞吐量测试的速度比Camel快,但我们的自定义JMS包装代码并未遇到此问题。也许代码更快,这意味着发布缓存更快被清空,或者我们在连接代码中覆盖了尚未找到的sendFailIfNoSpace。
关于速度问题,到目前为止,除了虚拟目的地之外,我们已经实现了这里提到的所有建议,但是在我的机器上,带有100K消息的Camel版本测试仍在16秒内运行,而我们自己的包装器则为10秒。如上所述,我有一种潜在的怀疑,即我们(隐式或其他方面)在我们的包装器中的某处覆盖了配置,但我怀疑它是否会导致ActiveMQ中的性能提升。
gwithake提到的虚拟目的地可能会加速这个特定的测试,但大部分时间我们的实际工作负载并不适合它。