使用AMQP建立到事件中心的连接时如何设置x-opt-offset以避免消息重播

时间:2019-05-17 22:43:19

标签: azure amqp azure-eventhub

我的应用程序连接到Azure事件中心以接收消息并进行处理。我看到每次重新启动应用程序时,保留期内的所有消息都会被重播。我读过有关偏移的内容,以避免出现此问题,并且我有一种方法将与Azure事件中心的连接设置为:

    MessageConsumer connect() {
        // set up JNDI context
        BatchEventHubConfig batchEventHubConfig = //MAP CONTAINING CONFIG
        String queueName = "EventHub"
        String connectionFactoryName = "SBCF"
        //Long offset = batchAccountManager.batchStorageManager.batchJobMsgCheckpointService.get(batchEventHubConfig.namespace, batchEventHubConfig.getMessageQueueAddress(partitionInx, true))?.offset
        Hashtable<String, String> hashtable = new Hashtable<>()
        hashtable.put("connectionfactory.${connectionFactoryName}", batchEventHubConfig.getAMQPConnectionURI())
        hashtable.put("queue.${queueName}", batchEventHubConfig.getMessageQueueAddress(partitionInx))
        //hashtable.put("apache.org:selector-filter:string", "amqp.annotation.x-opt-offset > '${offset}'")
        hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.amqp_1_0.jms.jndi.PropertiesFileInitialContextFactory")
        Context context = new InitialContext(hashtable)

        ConnectionFactory factory = (ConnectionFactory) context.lookup(connectionFactoryName)
        queue = (Destination) context.lookup(queueName)
        connection = factory.createConnection(batchEventHubConfig.sasPolicyName, batchEventHubConfig.sasPolicyKey)
        connection.setExceptionListener(new BatchExceptionListener(eventHubConnection: this))

        connection.start()
        session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE)
        messageConsumer = session.createConsumer(queue)
        messageConsumer.setMessageListener(messageListener)
        messageConsumer
    }

注释掉的偏移量代码是我在这里阅读以下内容后尝试的内容:https://azure.github.io/amqpnetlite/articles/azure_eventhubs.html

设置偏移量以便在应用程序重新启动时不重播消息的正确方法是什么?

3 个答案:

答案 0 :(得分:1)

Apche QPID不支持AMQP过滤器(基础Apache Proton J则支持)。

我通过在末尾添加以下几行来修补AmqpConsumerBuilder.configureSource()方法:

Symbol filterKey = Symbol.valueOf("apache.org:selector-filter:string");
UnknownDescribedType filterValue = new UnknownDescribedType(filterKey, String.format("%s > '%s'",amqp.annotation.x-opt-offset", lastOffset));

filters.put(filterKey, filterValue);

它有效!

因此,您可以派生Apache QPID并应用此补丁,或者将修改后的类放在类路径中以覆盖原始的(非常糟糕的解决方案)

答案 1 :(得分:0)

这比我想象的要容易得多!找到此链接:https://timjansen.github.io/jarfiller/guide/jms/selectors.xhtml

所以我所要做的就是添加这样的过滤条件:

messageConsumer = session.createConsumer(queue, "amqp.annotation.x-opt-offset >= '${messageOffset}'")

答案 2 :(得分:0)

受Mourad Zouabi答案的启发,我创建了qpid-jms存储库的派生,并如下更改了AmqpConsumerBuilder类,以允许将过滤器传递给MessageConsumer:

   if (resourceInfo.getSelector() != null && !resourceInfo.getSelector().trim().equals("")) {
        if (resourceInfo.getSelector().startsWith("x-opt-offset") || resourceInfo.getSelector().startsWith("x-opt-enqueued-time")) {
            // support Azure Event HUB filters
            // see: https://azure.github.io/amqpnetlite/articles/azure_eventhubs.html
            Symbol filterKey = Symbol.valueOf("apache.org:selector-filter:string");
            UnknownDescribedType filterValue = new UnknownDescribedType(filterKey,"amqp.annotation." + resourceInfo.getSelector().trim());
            filters.put(filterKey, filterValue);
        } else {
            filters.put(JMS_SELECTOR_SYMBOL, new AmqpJmsSelectorType(resourceInfo.getSelector()));
        }
    }
    if (!filters.isEmpty()) {
        source.setFilter(filters);
    }

现在,在创建MessageConsumer时可以通过过滤器:

MessageConsumer consumer = session.createConsumer(queue,  String.format("x-opt-offset > %s", offset));

MessageConsumer consumer = session.createConsumer(queue,  String.format("x-opt-enqueued-time > %s", timeStamp));

有关工作叉,请参见https://github.com/ekkelenkamp/qpid-jms