This is an automated email from the ASF dual-hosted git repository.

mikexue pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/eventmesh.git


The following commit(s) were added to refs/heads/master by this push:
     new 95ec17362 [ISSUE #4388] Improve the rocketmq source connector offset 
ack (#4389)
95ec17362 is described below

commit 95ec1736271a2bdd020b859cdc0119c7b951c8fb
Author: mike_xwm <[email protected]>
AuthorDate: Wed Aug 23 19:31:13 2023 +0800

    [ISSUE #4388] Improve the rocketmq source connector offset ack (#4389)
    
    * [ISSUE #4388] Improve the rocketmq source connector offset ack
    
    * update comments
---
 .../source/connector/RocketMQSourceConnector.java  | 92 ++++++++++++++++++++--
 .../apache/eventmesh/openconnect/SourceWorker.java |  2 +-
 2 files changed, 88 insertions(+), 6 deletions(-)

diff --git 
a/eventmesh-connectors/eventmesh-connector-rocketmq/src/main/java/org/apache/eventmesh/connector/rocketmq/source/connector/RocketMQSourceConnector.java
 
b/eventmesh-connectors/eventmesh-connector-rocketmq/src/main/java/org/apache/eventmesh/connector/rocketmq/source/connector/RocketMQSourceConnector.java
index 331641d22..8023be479 100644
--- 
a/eventmesh-connectors/eventmesh-connector-rocketmq/src/main/java/org/apache/eventmesh/connector/rocketmq/source/connector/RocketMQSourceConnector.java
+++ 
b/eventmesh-connectors/eventmesh-connector-rocketmq/src/main/java/org/apache/eventmesh/connector/rocketmq/source/connector/RocketMQSourceConnector.java
@@ -27,13 +27,23 @@ import 
org.apache.eventmesh.openconnect.offsetmgmt.api.data.RecordOffset;
 import org.apache.eventmesh.openconnect.offsetmgmt.api.data.RecordPartition;
 import 
org.apache.eventmesh.openconnect.offsetmgmt.api.storage.OffsetStorageReader;
 
+import org.apache.rocketmq.client.consumer.AllocateMessageQueueStrategy;
 import org.apache.rocketmq.client.consumer.DefaultLitePullConsumer;
+import 
org.apache.rocketmq.client.consumer.rebalance.AllocateMessageQueueAveragely;
+import org.apache.rocketmq.client.exception.MQBrokerException;
+import org.apache.rocketmq.client.exception.MQClientException;
 import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
 import org.apache.rocketmq.common.message.MessageExt;
 import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.common.protocol.body.Connection;
+import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
+import org.apache.rocketmq.remoting.exception.RemotingException;
+import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
 
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -45,6 +55,7 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
 
 import lombok.extern.slf4j.Slf4j;
 
@@ -55,6 +66,11 @@ public class RocketMQSourceConnector implements Source {
 
     private OffsetStorageReader offsetStorageReader;
 
+    private DefaultMQAdminExt srcMQAdminExt;
+
+    // message queue divided strategy
+    private final AllocateMessageQueueStrategy allocateMessageQueueStrategy = 
new AllocateMessageQueueAveragely();
+
     private final DefaultLitePullConsumer consumer = new 
DefaultLitePullConsumer();
 
     private final ScheduledExecutorService commitOffsetScheduleService = 
Executors.newSingleThreadScheduledExecutor();
@@ -71,7 +87,7 @@ public class RocketMQSourceConnector implements Source {
     }
 
     @Override
-    public void init(Config config) {
+    public void init(Config config) throws Exception {
         // init config for rocketmq source connector
         this.sourceConfig = (RocketMQSourceConfig) config;
         consumer.setConsumerGroup(sourceConfig.getPubSubConfig().getGroup());
@@ -79,6 +95,7 @@ public class RocketMQSourceConnector implements Source {
         consumer.setAutoCommit(false);
         consumer.setPullBatchSize(32);
         
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
+        initAdmin();
     }
 
     @Override
@@ -91,15 +108,79 @@ public class RocketMQSourceConnector implements Source {
         consumer.setAutoCommit(false);
         consumer.setPullBatchSize(32);
         
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
+        consumer.setAllocateMessageQueueStrategy(allocateMessageQueueStrategy);
+        initAdmin();
+    }
+
+    private synchronized void initAdmin() throws MQClientException {
+        if (srcMQAdminExt == null) {
+            srcMQAdminExt = new DefaultMQAdminExt();
+            
srcMQAdminExt.setNamesrvAddr(sourceConfig.getConnectorConfig().getNameserver());
+            srcMQAdminExt.setAdminExtGroup("RocketMQ-Admin");
+        }
     }
 
     @Override
     public void start() throws Exception {
-        consumer.subscribe(sourceConfig.getConnectorConfig().getTopic(), "*");
+
+        consumer.start();
+        srcMQAdminExt.start();
+
         // commit offset with schedule task
         execScheduleTask();
-        consumer.start();
-        // todo: if we can specify the certain offset for consumption
+        // todo: we need more elegant way instead of sleep
+        // for rocketmq client, will delay 1 second to send heartbeat to 
broker, so here sleep few seconds
+        Thread.sleep(1500);
+
+        List<MessageQueue> allocated = 
getAllocatedMessageQueue(sourceConfig.getConnectorConfig().getTopic(),
+            sourceConfig.getPubSubConfig().getGroup());
+
+        consumer.assign(allocated);
+
+        consumer.setMessageQueueListener((topic, mqAll, mqDivided) -> {
+
+            for (MessageQueue messageQueue : mqDivided) {
+                try {
+                    Map<String, String> partitionMap = new HashMap<>();
+                    partitionMap.put("topic", messageQueue.getTopic());
+                    partitionMap.put("brokerName", 
messageQueue.getBrokerName());
+                    partitionMap.put("queueId", messageQueue.getQueueId() + 
"");
+                    RecordPartition recordPartition = new 
RecordPartition(partitionMap);
+                    RecordOffset recordOffset = 
offsetStorageReader.readOffset(recordPartition);
+                    log.info("assigned messageQueue {}, recordOffset {}", 
messageQueue, recordOffset);
+                    if (recordOffset != null) {
+                        long pollOffset = (Long) 
recordOffset.getOffset().get("queueOffset");
+                        if (pollOffset != 0) {
+                            consumer.seek(messageQueue, pollOffset);
+                        }
+                    }
+                } catch (MQClientException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+    }
+
+    private List<MessageQueue> getAllocatedMessageQueue(String topic, String 
group)
+        throws MQBrokerException, RemotingException, InterruptedException, 
MQClientException {
+        List<MessageQueue> mqAll = getMessageQueueList(topic);
+        List<String> cidAll = getCidList(group);
+        if (cidAll != null) {
+            Collections.sort(mqAll);
+            Collections.sort(cidAll);
+            return allocateMessageQueueStrategy.allocate(group, 
consumer.buildMQClientId(), mqAll, cidAll);
+        }
+        return new ArrayList<>();
+    }
+
+    private List<String> getCidList(String group) throws MQBrokerException, 
RemotingException, InterruptedException, MQClientException {
+        ConsumerConnection consumerConnection = 
srcMQAdminExt.examineConsumerConnectionInfo(group);
+        return 
consumerConnection.getConnectionSet().stream().map(Connection::getClientId).collect(Collectors.toList());
+    }
+
+    private List<MessageQueue> getMessageQueueList(String topic) throws 
MQClientException {
+        Collection<MessageQueue> messageQueueCollection = 
consumer.fetchMessageQueues(topic);
+        return new ArrayList<>(messageQueueCollection);
     }
 
     @Override
@@ -212,11 +293,12 @@ public class RocketMQSourceConnector implements Source {
         if (canCommitOffset == -1) {
             return;
         }
+        long nextBeginOffset = canCommitOffset + 1;
         List<AtomicLong> commitOffset = prepareCommitOffset.get(mq);
         if (commitOffset == null || commitOffset.isEmpty()) {
             commitOffset = new ArrayList<>();
         }
-        commitOffset.add(new AtomicLong(canCommitOffset));
+        commitOffset.add(new AtomicLong(nextBeginOffset));
         prepareCommitOffset.put(mq, commitOffset);
     }
 }
diff --git 
a/eventmesh-openconnect/eventmesh-openconnect-java/src/main/java/org/apache/eventmesh/openconnect/SourceWorker.java
 
b/eventmesh-openconnect/eventmesh-openconnect-java/src/main/java/org/apache/eventmesh/openconnect/SourceWorker.java
index aacb44b4b..a1fbc08b1 100644
--- 
a/eventmesh-openconnect/eventmesh-openconnect-java/src/main/java/org/apache/eventmesh/openconnect/SourceWorker.java
+++ 
b/eventmesh-openconnect/eventmesh-openconnect-java/src/main/java/org/apache/eventmesh/openconnect/SourceWorker.java
@@ -156,7 +156,7 @@ public class SourceWorker implements ConnectorWorker {
                     startConnector();
                 } catch (Exception e) {
                     log.error("source worker[{}] start fail", source.name(), 
e);
-                    throw new RuntimeException(e);
+                    this.stop();
                 }
             }
         );


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to