简体   繁体   English

ActiveMQ上的重复消息

[英]Duplicated messages on ActiveMQ

I use ActiveMQ as JMS broker and consumer, jmsTemplate to send the messages, 1 non-durable Topic for the moment. 我使用ActiveMQ作为JMS代理和消费者,使用jmsTemplate发送消息,暂时使用1个非持久性主题。 During the peak time I have ~100 messages/second. 在高峰时间我有~100条消息/秒。

It doesn't matter how many messages are in the queue, but I frequently get duplicated messages. 队列中有多少消息无关紧要,但我经常会收到重复的消息。 The temporary solution that I came up is to set up index on table - for the moment all messages are only saved in database. 我提出的临时解决方案是在表上设置索引 - 目前所有消息都只保存在数据库中。

My first question - why messages are duplicated, if I specified non-durable Topic and the response is not required? 我的第一个问题 - 为什么消息重复,如果我指定非持久主题并且不需要响应?

Sender: 发件人:

@Component
public class QueueSender 
{
    private Logger log = Logger.getLogger(getClass());
@Autowired
    protected JmsTemplate jmsTemplate;


    public JmsTemplate getJmsTemplate() {
        return jmsTemplate;
    }

    public void setJmsTemplate(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    @Autowired
    public QueueSender( final JmsTemplate jmsTemplate )
    {
        this.jmsTemplate = jmsTemplate;
        this.jmsTemplate.setDeliveryPersistent(false);
        System.out.println("isSessionTransacted "+jmsTemplate.isSessionTransacted()+
                " getDeliveryMode "+jmsTemplate.getDeliveryMode()+
                " getReceiveTimeout "+jmsTemplate.getReceiveTimeout()+
                " getSessionAcknowledgeMode "+jmsTemplate.getSessionAcknowledgeMode());
    }


    public void sendPrice(Integer tickerId, Integer field, Double price, Long timestamp)
    {
        jmsTemplate.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
        jmsTemplate.setMessageIdEnabled(true);
        Map <String, Object>map = new HashMap<String, Object>();
        map.put("tickerId", tickerId);
        map.put("field", field);
        map.put("price", price);
        map.put("timestamp", timestamp);
        jmsTemplate.convertAndSend("Quotez", map);
    }

    public void sendVolume(Integer tickerId, Integer field, Integer size, Long timestamp)
    {
        jmsTemplate.setDeliveryMode(DeliveryMode.NON_PERSISTENT);

        Map <String, Object>map = new HashMap<String, Object>();
        map.put("tickerId", tickerId);
        map.put("field", field);
        map.put("size", size);
        map.put("timestamp", timestamp);
        jmsTemplate.convertAndSend("Quotez", map);

    }

}

Listener: 监听器:

public void onMessage(Message message) 
{
     if (message instanceof MapMessage) 
     {           
         try
         {
             MapMessage mapMessage = (MapMessage) message;
                 if(null !=  mapMessage.getString("price"))
                 {
 priceService.insert(mapMessage.getInt("tickerId"),mapMessage.getDouble("price"),
mapMessage.getInt("field"),mapMessage.getLong("timestamp"));
                 }                     else{
volumeService.insert(mapMessage.getInt("tickerId"),mapMessage.getInt("size"),
mapMessage.getInt("field"),mapMessage.getLong("timestamp"));
             }
         }
         catch (final JMSException e)
         {
             exceptionListener.onException(e);
         }
     }
}

Spring: 弹簧:

<amq:broker useJmx="true" persistent="false">
<amq:transportConnectors>
  <amq:transportConnector uri="tcp://localhost:0"/>
</amq:transportConnectors> </amq:broker>
<amq:topic id="topicDest"  physicalName="Quotez"/>
  <amq:connectionFactory id="jmsFactory" brokerURL="vm://localhost?jms.watchTopicAdvisories=false"/>  
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<constructor-arg ref="jmsFactory" />
<property name="exceptionListener" ref="jmsExceptionListener" />
<property name="sessionCacheSize" value="100" />
</bean>


<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
   <constructor-arg ref="connectionFactory"/>
    <property name="pubSubDomain" value="true"/>
 <property name="defaultDestinationName" value="Quotez"/>    
</bean>
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory"/>
        <property name="destination" ref="topicDest"/>
        <property name="messageListener" ref="jdbcListener" />
    </bean>

The second question is concerning jmsContainer configuration. 第二个问题是关于jmsContainer配置。 What is the difference between the code above and the code below? 上面的代码和下面的代码有什么区别? The code above gives me Topic as subscriber and the code below gives me Queue. 上面的代码给了我作为订阅者的主题,下面的代码给了我Queue。

<jms:listener-container concurrency="10" connection-factory="connectionFactory">    
<jms:listener id="JdbcListener" destination="topicDest" ref="queueListener" />  
</jms:listener-container>

I found, that Camel and its idempotentConsumer suppose to solve duplication problem - of course, it would be nice to know why it happens in first place. 我发现,Camel及其幂等消费者想要解决重复问题 - 当然,知道为什么它首先发生会很好。 The third question concerns Camel's configuration. 第三个问题涉及Camel的配置。 I have this configuration (default): 我有这个配置(默认):

<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="brokerURL" value="tcp://localhost:0"/>
</bean>

<bean id="myRepo" class="org.apache.camel.processor.idempotent.MemoryIdempotentRepository"/>

<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
    <from uri="direct:start"/>
    <idempotentConsumer messageIdRepositoryRef="myRepo">
        <header>messageId</header>
        <to uri="mock:result"/>
    </idempotentConsumer>
</route>
</camelContext>

Does it apply for all queues or should I make explicit subscription? 它是否适用于所有队列或我应该明确订阅? I suppose it will check every topic/queue and all incoming messages. 我想它会检查每个主题/队列和所有传入的消息。 The problem at the moment, that all messages have messageId=null and the filter takes it as the parameter. 目前的问题是,所有消息都有messageId = null,并且过滤器将其作为参数。

2011-03-01 11:24:09,152 DEBUG (org.springframework.jms.core.JmsTemplate:567) - Sending created message: ActiveMQMapMessage {commandId = 0, responseRequired = false, **messageId = null**, originalDestination = null, originalTransactionId = null, producerId = null, destination = null, transactionId = null, expiration = 0, timestamp = 0, arrival = 0, brokerInTime = 0, brokerOutTime = 0, correlationId = null, replyTo = null, persistent = false, type = null, priority = 0, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = null, marshalledProperties = null, dataStructure = null, redeliveryCounter = 0, size = 0, properties = null, readOnlyProperties = false, readOnlyBody = false, droppable = false} ActiveMQMapMessage{ theTable = {field=1, timestamp=1298975049138, price=72.89, tickerId=2} }

I didn't find a easy way of setting messageId. 我找不到设置messageId的简单方法。 My question - is it enough to set messageId and it will work as excepted or something is wrong with configuration, for example I have to specify which topic will be used. 我的问题 - 是否足以设置messageId,它将作为例外工作或配置有问题,例如我必须指定将使用哪个主题。

Thanks, 谢谢,

Dzidas Dzidas

when using a JMS topic, you need to set the concurrent/max concurrent consumers to "1" or you will get duplicates. 使用JMS主题时,您需要将并发/最大并发使用者设置为“1”,否则您将获得重复项。 if you need multi-threaded consumption and/or load balancing, then use virtual topics instead. 如果您需要多线程消费和/或负载平衡,则使用虚拟主题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM