|
@@ -1,11 +1,7 @@
|
|
|
package com.alibaba.otter.canal.client.rocketmq;
|
|
|
|
|
|
-import com.alibaba.fastjson.JSON;
|
|
|
-import com.alibaba.otter.canal.protocol.FlatMessage;
|
|
|
import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
import java.util.concurrent.BlockingQueue;
|
|
|
-import java.util.concurrent.ConcurrentHashMap;
|
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
@@ -19,35 +15,47 @@ import org.apache.rocketmq.common.message.MessageExt;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
-import com.alibaba.otter.canal.client.CanalConnector;
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.otter.canal.client.CanalMQConnector;
|
|
|
import com.alibaba.otter.canal.client.CanalMessageDeserializer;
|
|
|
+import com.alibaba.otter.canal.client.impl.SimpleCanalConnector;
|
|
|
+import com.alibaba.otter.canal.protocol.FlatMessage;
|
|
|
import com.alibaba.otter.canal.protocol.Message;
|
|
|
import com.alibaba.otter.canal.protocol.exception.CanalClientException;
|
|
|
-
|
|
|
-public class RocketMQCanalConnector implements CanalConnector {
|
|
|
-
|
|
|
- private static final Logger logger = LoggerFactory.getLogger(RocketMQCanalConnector.class);
|
|
|
-
|
|
|
- private String nameServer;
|
|
|
- private String topic;
|
|
|
- private String groupName;
|
|
|
- private volatile boolean connected = false;
|
|
|
- private DefaultMQPushConsumer rocketMQConsumer;
|
|
|
+import com.google.common.collect.Lists;
|
|
|
+
|
|
|
+/**
|
|
|
+ * RocketMQ的连接
|
|
|
+ *
|
|
|
+ * <pre>
|
|
|
+ * 注意点:
|
|
|
+ * 1. 相比于canal {@linkplain SimpleCanalConnector}, 这里get和ack操作不能有并发, 必须是一个线程执行get后,内存里执行完毕ack后再取下一个get
|
|
|
+ * </pre>
|
|
|
+ *
|
|
|
+ * @since 1.1.1
|
|
|
+ */
|
|
|
+public class RocketMQCanalConnector implements CanalMQConnector {
|
|
|
+
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(RocketMQCanalConnector.class);
|
|
|
+
|
|
|
+ private String nameServer;
|
|
|
+ private String topic;
|
|
|
+ private String groupName;
|
|
|
+ private volatile boolean connected = false;
|
|
|
+ private DefaultMQPushConsumer rocketMQConsumer;
|
|
|
private BlockingQueue<ConsumerBatchMessage> messageBlockingQueue;
|
|
|
- Map<Long, ConsumerBatchMessage> messageCache;
|
|
|
- private long batchProcessTimeout = 3000;
|
|
|
- private boolean flatMessage;
|
|
|
+ private long batchProcessTimeout = 60 * 1000;
|
|
|
+ private boolean flatMessage;
|
|
|
+ private volatile ConsumerBatchMessage lastGetBatchMessage = null;
|
|
|
|
|
|
- public RocketMQCanalConnector(String nameServer, String topic, String groupName, boolean flatMessage) {
|
|
|
+ public RocketMQCanalConnector(String nameServer, String topic, String groupName, boolean flatMessage){
|
|
|
this.nameServer = nameServer;
|
|
|
this.topic = topic;
|
|
|
this.groupName = groupName;
|
|
|
this.flatMessage = flatMessage;
|
|
|
- messageBlockingQueue = new LinkedBlockingQueue<>();
|
|
|
- messageCache = new ConcurrentHashMap<>();
|
|
|
+ this.messageBlockingQueue = new LinkedBlockingQueue<>(1024);
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
public void connect() throws CanalClientException {
|
|
|
rocketMQConsumer = new DefaultMQPushConsumer(groupName);
|
|
|
if (!StringUtils.isBlank(nameServer)) {
|
|
@@ -55,17 +63,14 @@ public class RocketMQCanalConnector implements CanalConnector {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
public void disconnect() throws CanalClientException {
|
|
|
rocketMQConsumer.shutdown();
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
public boolean checkValid() throws CanalClientException {
|
|
|
return connected;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
public synchronized void subscribe(String filter) throws CanalClientException {
|
|
|
if (connected) {
|
|
|
return;
|
|
@@ -78,8 +83,7 @@ public class RocketMQCanalConnector implements CanalConnector {
|
|
|
rocketMQConsumer.registerMessageListener(new MessageListenerOrderly() {
|
|
|
|
|
|
@Override
|
|
|
- public ConsumeOrderlyStatus consumeMessage(List<MessageExt> messageExts,
|
|
|
- ConsumeOrderlyContext context) {
|
|
|
+ public ConsumeOrderlyStatus consumeMessage(List<MessageExt> messageExts, ConsumeOrderlyContext context) {
|
|
|
context.setAutoCommit(true);
|
|
|
boolean isSuccess = process(messageExts);
|
|
|
if (isSuccess) {
|
|
@@ -99,23 +103,23 @@ public class RocketMQCanalConnector implements CanalConnector {
|
|
|
|
|
|
private boolean process(List<MessageExt> messageExts) {
|
|
|
logger.info("Get Message:{}", messageExts);
|
|
|
- BlockingQueue messageList = new LinkedBlockingQueue<>();
|
|
|
+ List messageList = Lists.newArrayList();
|
|
|
for (MessageExt messageExt : messageExts) {
|
|
|
byte[] data = messageExt.getBody();
|
|
|
- if (data != null){
|
|
|
+ if (data != null) {
|
|
|
try {
|
|
|
if (!flatMessage) {
|
|
|
Message message = CanalMessageDeserializer.deserializer(data);
|
|
|
- messageList.put(message);
|
|
|
+ messageList.add(message);
|
|
|
} else {
|
|
|
FlatMessage flatMessage = JSON.parseObject(data, FlatMessage.class);
|
|
|
- messageList.put(flatMessage);
|
|
|
+ messageList.add(flatMessage);
|
|
|
}
|
|
|
} catch (Exception ex) {
|
|
|
logger.error("Add message error", ex);
|
|
|
throw new CanalClientException(ex);
|
|
|
}
|
|
|
- }else{
|
|
|
+ } else {
|
|
|
logger.warn("Received message data is null");
|
|
|
}
|
|
|
}
|
|
@@ -142,145 +146,117 @@ public class RocketMQCanalConnector implements CanalConnector {
|
|
|
return isCompleted && isSuccess;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
public void subscribe() throws CanalClientException {
|
|
|
this.subscribe(null);
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
public void unsubscribe() throws CanalClientException {
|
|
|
this.rocketMQConsumer.unsubscribe(this.topic);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 暂时不支持batchSize 参数
|
|
|
- *
|
|
|
- * @param batchSize 暂时不支持
|
|
|
- * @return
|
|
|
- * @throws CanalClientException
|
|
|
- */
|
|
|
@Override
|
|
|
- public Message get(int batchSize) throws CanalClientException {
|
|
|
- Message message = getWithoutAck(batchSize);
|
|
|
- if (message != null) {
|
|
|
- ack(message.getId());
|
|
|
+ public List<Message> getList(Long timeout, TimeUnit unit) throws CanalClientException {
|
|
|
+ List<Message> messages = getListWithoutAck(timeout, unit);
|
|
|
+ if (messages != null && !messages.isEmpty()) {
|
|
|
+ ack();
|
|
|
}
|
|
|
- return message;
|
|
|
+ return messages;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public Message get(int batchSize, Long timeout, TimeUnit unit) throws CanalClientException {
|
|
|
- Message message = getWithoutAck(batchSize, timeout, unit);
|
|
|
- if (message != null) {
|
|
|
- ack(message.getId());
|
|
|
- }
|
|
|
- return message;
|
|
|
- }
|
|
|
-
|
|
|
- private Message getMessage(ConsumerBatchMessage consumerBatchMessage) {
|
|
|
- BlockingQueue<Message> messageList = consumerBatchMessage.getData();
|
|
|
- if (messageList != null & messageList.size() > 0) {
|
|
|
- Message message = messageList.poll();
|
|
|
- messageCache.put(message.getId(), consumerBatchMessage);
|
|
|
- return message;
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
+ public List<Message> getListWithoutAck(Long timeout, TimeUnit unit) throws CanalClientException {
|
|
|
+ try {
|
|
|
+ if (this.lastGetBatchMessage != null) {
|
|
|
+ throw new CanalClientException("mq get/ack not support concurrent & async ack");
|
|
|
+ }
|
|
|
|
|
|
- private FlatMessage getFlatMessage(ConsumerBatchMessage consumerBatchMessage) {
|
|
|
- BlockingQueue<FlatMessage> messageList = consumerBatchMessage.getData();
|
|
|
- if (messageList != null & messageList.size() > 0) {
|
|
|
- FlatMessage message = messageList.poll();
|
|
|
- messageCache.put(message.getId(), consumerBatchMessage);
|
|
|
- return message;
|
|
|
+ ConsumerBatchMessage batchMessage = messageBlockingQueue.poll(timeout, unit);
|
|
|
+ if (batchMessage != null) {
|
|
|
+ this.lastGetBatchMessage = batchMessage;
|
|
|
+ return batchMessage.getData();
|
|
|
+ }
|
|
|
+ } catch (InterruptedException ex) {
|
|
|
+ logger.warn("Get message timeout", ex);
|
|
|
+ throw new CanalClientException("Failed to fetch the data after: " + timeout);
|
|
|
}
|
|
|
- return null;
|
|
|
+ return Lists.newArrayList();
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 暂时不支持该参数设置
|
|
|
- *
|
|
|
- * @param batchSize
|
|
|
- * @return
|
|
|
- * @throws CanalClientException
|
|
|
- */
|
|
|
@Override
|
|
|
- public Message getWithoutAck(int batchSize) throws CanalClientException {
|
|
|
- ConsumerBatchMessage batchMessage = messageBlockingQueue.poll();
|
|
|
- if (batchMessage != null) {
|
|
|
- return getMessage(batchMessage);
|
|
|
+ public List<FlatMessage> getFlatList(Long timeout, TimeUnit unit) throws CanalClientException {
|
|
|
+ List<FlatMessage> messages = getFlatListWithoutAck(timeout, unit);
|
|
|
+ if (messages != null && !messages.isEmpty()) {
|
|
|
+ ack();
|
|
|
}
|
|
|
- return null;
|
|
|
+ return messages;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public Message getWithoutAck(int batchSize, Long timeout, TimeUnit unit) throws CanalClientException {
|
|
|
+ public List<FlatMessage> getFlatListWithoutAck(Long timeout, TimeUnit unit) throws CanalClientException {
|
|
|
try {
|
|
|
+ if (this.lastGetBatchMessage != null) {
|
|
|
+ throw new CanalClientException("mq get/ack not support concurrent & async ack");
|
|
|
+ }
|
|
|
+
|
|
|
ConsumerBatchMessage batchMessage = messageBlockingQueue.poll(timeout, unit);
|
|
|
if (batchMessage != null) {
|
|
|
- return getMessage(batchMessage);
|
|
|
+ this.lastGetBatchMessage = batchMessage;
|
|
|
+ return batchMessage.getData();
|
|
|
}
|
|
|
} catch (InterruptedException ex) {
|
|
|
logger.warn("Get message timeout", ex);
|
|
|
throw new CanalClientException("Failed to fetch the data after: " + timeout);
|
|
|
}
|
|
|
- return null;
|
|
|
+ return Lists.newArrayList();
|
|
|
}
|
|
|
|
|
|
- public FlatMessage getFlatMessageWithoutAck() {
|
|
|
- return getFlatMessageWithoutAck(null, null);
|
|
|
- }
|
|
|
-
|
|
|
- public FlatMessage getFlatMessageWithoutAck(Long timeout,
|
|
|
- TimeUnit unit) throws CanalClientException {
|
|
|
+ @Override
|
|
|
+ public void ack() throws CanalClientException {
|
|
|
try {
|
|
|
- ConsumerBatchMessage batchMessage = null;
|
|
|
- if (timeout == null || timeout == 0) {
|
|
|
- batchMessage = messageBlockingQueue.poll();
|
|
|
- } else {
|
|
|
- batchMessage = messageBlockingQueue.poll(timeout, unit);
|
|
|
- }
|
|
|
- if (batchMessage != null) {
|
|
|
- return getFlatMessage(batchMessage);
|
|
|
- }
|
|
|
- } catch (InterruptedException ex) {
|
|
|
- logger.warn("Get flat message timeout", ex);
|
|
|
- throw new CanalClientException("Failed to fetch the flat message data after: " + timeout);
|
|
|
+ this.lastGetBatchMessage.ack();
|
|
|
+ } catch (Throwable e) {
|
|
|
+ this.lastGetBatchMessage.fail();
|
|
|
+ } finally {
|
|
|
+ this.lastGetBatchMessage = null;
|
|
|
}
|
|
|
- return null;
|
|
|
}
|
|
|
|
|
|
- public FlatMessage getFlatMessage() throws CanalClientException {
|
|
|
- FlatMessage message = getFlatMessageWithoutAck(null, null);
|
|
|
- if (message != null) {
|
|
|
- ack(message.getId());
|
|
|
+ @Override
|
|
|
+ public void rollback() throws CanalClientException {
|
|
|
+ try {
|
|
|
+ this.lastGetBatchMessage.fail();
|
|
|
+ } finally {
|
|
|
+ this.lastGetBatchMessage = null;
|
|
|
}
|
|
|
- return message;
|
|
|
}
|
|
|
|
|
|
- public FlatMessage getFlatMessage(Long timeout, TimeUnit unit) throws CanalClientException {
|
|
|
- FlatMessage message = getFlatMessageWithoutAck(timeout, unit);
|
|
|
- ack(message.getId());
|
|
|
- return message;
|
|
|
+ public Message get(int batchSize) throws CanalClientException {
|
|
|
+ throw new CanalClientException("mq not support this method");
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void ack(long batchId) throws CanalClientException {
|
|
|
- ConsumerBatchMessage batchMessage = messageCache.get(batchId);
|
|
|
- if (batchMessage != null) {
|
|
|
- batchMessage.ack();
|
|
|
- messageCache.remove(batchId);
|
|
|
- }
|
|
|
+ public Message get(int batchSize, Long timeout, TimeUnit unit) throws CanalClientException {
|
|
|
+ throw new CanalClientException("mq not support this method");
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void rollback(long batchId) throws CanalClientException {
|
|
|
+ public Message getWithoutAck(int batchSize) throws CanalClientException {
|
|
|
+ throw new CanalClientException("mq not support this method");
|
|
|
+ }
|
|
|
|
|
|
+ @Override
|
|
|
+ public Message getWithoutAck(int batchSize, Long timeout, TimeUnit unit) throws CanalClientException {
|
|
|
+ throw new CanalClientException("mq not support this method");
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public void rollback() throws CanalClientException {
|
|
|
+ public void ack(long batchId) throws CanalClientException {
|
|
|
+ throw new CanalClientException("mq not support this method");
|
|
|
+ }
|
|
|
|
|
|
+ @Override
|
|
|
+ public void rollback(long batchId) throws CanalClientException {
|
|
|
+ throw new CanalClientException("mq not support this method");
|
|
|
}
|
|
|
|
|
|
@Override
|