This commit is contained in:
jianghang.loujh
2024-05-22 09:46:13 +08:00
parent 5ff6449620
commit aa05d8e2a0
8 changed files with 180 additions and 169 deletions

View File

@@ -65,8 +65,9 @@ public class ES8xTemplate implements ESTemplate {
if (mapping.getId() != null) { if (mapping.getId() != null) {
String parentVal = (String) esFieldData.remove("$parent_routing"); String parentVal = (String) esFieldData.remove("$parent_routing");
if (mapping.isUpsert()) { if (mapping.isUpsert()) {
ESUpdateRequest updateRequest = esConnection.new ES8xUpdateRequest(mapping.getIndex(), ESUpdateRequest updateRequest = esConnection.new ES8xUpdateRequest(mapping.getIndex(), pkVal.toString())
pkVal.toString()).setDoc(esFieldData).setDocAsUpsert(true); .setDoc(esFieldData)
.setDocAsUpsert(true);
if (StringUtils.isNotEmpty(parentVal)) { if (StringUtils.isNotEmpty(parentVal)) {
updateRequest.setRouting(parentVal); updateRequest.setRouting(parentVal);
} }

View File

@@ -1,6 +1,21 @@
package com.alibaba.otter.canal.client.adapter.es8x.support; package com.alibaba.otter.canal.client.adapter.es8x.support;
import com.alibaba.otter.canal.client.adapter.es.core.support.ESBulkRequest; import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.Map;
import javax.net.ssl.SSLContext;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthScope;
@@ -32,20 +47,7 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext; import com.alibaba.otter.canal.client.adapter.es.core.support.ESBulkRequest;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.Map;
/** /**
* ES 连接器, 只支持 Rest 方式 * ES 连接器, 只支持 Rest 方式
@@ -59,7 +61,7 @@ public class ESConnection {
private RestHighLevelClient restHighLevelClient; private RestHighLevelClient restHighLevelClient;
public ESConnection(String[] hosts, Map<String, String> properties) throws UnknownHostException { public ESConnection(String[] hosts, Map<String, String> properties) throws UnknownHostException{
String caPath = properties.get("security.ca.path"); String caPath = properties.get("security.ca.path");
if (StringUtils.isNotEmpty(caPath)) { if (StringUtils.isNotEmpty(caPath)) {
connectEsWithCa(hosts, properties, caPath); connectEsWithCa(hosts, properties, caPath);
@@ -67,6 +69,7 @@ public class ESConnection {
connectEsWithoutCa(hosts, properties); connectEsWithoutCa(hosts, properties);
} }
} }
private void connectEsWithCa(String[] hosts, Map<String, String> properties, String caPath) { private void connectEsWithCa(String[] hosts, Map<String, String> properties, String caPath) {
Path caCertificatePath = Paths.get(caPath); Path caCertificatePath = Paths.get(caPath);
try (InputStream is = Files.newInputStream(caCertificatePath)) { try (InputStream is = Files.newInputStream(caCertificatePath)) {
@@ -75,8 +78,7 @@ public class ESConnection {
KeyStore trustStore = KeyStore.getInstance("pkcs12"); KeyStore trustStore = KeyStore.getInstance("pkcs12");
trustStore.load(null, null); trustStore.load(null, null);
trustStore.setCertificateEntry("ca", trustedCa); trustStore.setCertificateEntry("ca", trustedCa);
SSLContextBuilder sslContextBuilder = SSLContexts.custom() SSLContextBuilder sslContextBuilder = SSLContexts.custom().loadTrustMaterial(trustStore, null);
.loadTrustMaterial(trustStore, null);
final SSLContext sslContext = sslContextBuilder.build(); final SSLContext sslContext = sslContextBuilder.build();
HttpHost[] httpHosts = Arrays.stream(hosts).map(this::createHttpHost).toArray(HttpHost[]::new); HttpHost[] httpHosts = Arrays.stream(hosts).map(this::createHttpHost).toArray(HttpHost[]::new);
@@ -86,13 +88,15 @@ public class ESConnection {
String[] nameAndPwdArr = nameAndPwd.split(":"); String[] nameAndPwdArr = nameAndPwd.split(":");
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(nameAndPwdArr[0], nameAndPwdArr[1])); new UsernamePasswordCredentials(nameAndPwdArr[0], nameAndPwdArr[1]));
restClientBuilder.setHttpClientConfigCallback(httpClientBuilder -> { restClientBuilder.setHttpClientConfigCallback(httpClientBuilder -> {
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
return httpClientBuilder.setSSLContext(sslContext); return httpClientBuilder.setSSLContext(sslContext);
}); });
} }
restHighLevelClient = new RestHighLevelClientBuilder(restClientBuilder.build()).setApiCompatibilityMode(true).build(); restHighLevelClient = new RestHighLevelClientBuilder(restClientBuilder.build())
.setApiCompatibilityMode(true)
.build();
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -106,12 +110,12 @@ public class ESConnection {
String[] nameAndPwdArr = nameAndPwd.split(":"); String[] nameAndPwdArr = nameAndPwd.split(":");
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(nameAndPwdArr[0], nameAndPwdArr[1])); new UsernamePasswordCredentials(nameAndPwdArr[0], nameAndPwdArr[1]));
restClientBuilder.setHttpClientConfigCallback( restClientBuilder.setHttpClientConfigCallback(
httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)); httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
} }
restHighLevelClient = new RestHighLevelClientBuilder(restClientBuilder.build()).setApiCompatibilityMode(true) restHighLevelClient = new RestHighLevelClientBuilder(restClientBuilder.build()).setApiCompatibilityMode(true)
.build(); .build();
} }
public void close() { public void close() {
@@ -147,9 +151,9 @@ public class ESConnection {
private IndexRequestBuilder indexRequestBuilder; private IndexRequestBuilder indexRequestBuilder;
private IndexRequest indexRequest; private IndexRequest indexRequest;
public ES8xIndexRequest(String index, String id) { public ES8xIndexRequest(String index, String id){
indexRequest = new IndexRequest(index); indexRequest = new IndexRequest(index);
indexRequest.id(id); indexRequest.id(id);
@@ -190,9 +194,9 @@ public class ESConnection {
private UpdateRequestBuilder updateRequestBuilder; private UpdateRequestBuilder updateRequestBuilder;
private UpdateRequest updateRequest; private UpdateRequest updateRequest;
public ES8xUpdateRequest(String index, String id) { public ES8xUpdateRequest(String index, String id){
updateRequest = new UpdateRequest(index, id); updateRequest = new UpdateRequest(index, id);
} }
@@ -239,9 +243,9 @@ public class ESConnection {
private DeleteRequestBuilder deleteRequestBuilder; private DeleteRequestBuilder deleteRequestBuilder;
private DeleteRequest deleteRequest; private DeleteRequest deleteRequest;
public ES8xDeleteRequest(String index, String id) { public ES8xDeleteRequest(String index, String id){
deleteRequest = new DeleteRequest(index, id); deleteRequest = new DeleteRequest(index, id);
@@ -268,11 +272,11 @@ public class ESConnection {
private SearchRequestBuilder searchRequestBuilder; private SearchRequestBuilder searchRequestBuilder;
private SearchRequest searchRequest; private SearchRequest searchRequest;
private SearchSourceBuilder sourceBuilder; private SearchSourceBuilder sourceBuilder;
public ESSearchRequest(String index) { public ESSearchRequest(String index){
searchRequest = new SearchRequest(index); searchRequest = new SearchRequest(index);
sourceBuilder = new SearchSourceBuilder(); sourceBuilder = new SearchSourceBuilder();
@@ -325,12 +329,10 @@ public class ESConnection {
private BulkRequestBuilder bulkRequestBuilder; private BulkRequestBuilder bulkRequestBuilder;
private BulkRequest bulkRequest; private BulkRequest bulkRequest;
public ES8xBulkRequest() {
public ES8xBulkRequest(){
bulkRequest = new BulkRequest(); bulkRequest = new BulkRequest();
} }
public void resetBulk() { public void resetBulk() {
@@ -398,7 +400,7 @@ public class ESConnection {
private BulkResponse bulkResponse; private BulkResponse bulkResponse;
public ES8xBulkResponse(BulkResponse bulkResponse) { public ES8xBulkResponse(BulkResponse bulkResponse){
this.bulkResponse = bulkResponse; this.bulkResponse = bulkResponse;
} }
@@ -438,7 +440,7 @@ public class ESConnection {
} }
try { try {
return HttpHost.create(new URI(uri return HttpHost.create(new URI(uri
.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment()) .getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment())
.toString()); .toString());
} catch (URISyntaxException ex) { } catch (URISyntaxException ex) {
throw new IllegalStateException(ex); throw new IllegalStateException(ex);

View File

@@ -25,97 +25,98 @@ import com.google.common.collect.Lists;
* 注意点: * 注意点:
* 1. 相比于canal {@linkplain SimpleCanalConnector}, 这里get和ack操作不能有并发, 必须是一个线程执行get后,内存里执行完毕ack后再取下一个get * 1. 相比于canal {@linkplain SimpleCanalConnector}, 这里get和ack操作不能有并发, 必须是一个线程执行get后,内存里执行完毕ack后再取下一个get
* </pre> * </pre>
*
* todo 重复消费的概率相当高。一次批处理中,只要有一个消息处理失败,则该批次全部重试 * todo 重复消费的概率相当高。一次批处理中,只要有一个消息处理失败,则该批次全部重试
* *
* @since 1.1.1 * @since 1.1.1
*/ */
public class PulsarMQCanalConnector implements CanalMQConnector { public class PulsarMQCanalConnector implements CanalMQConnector {
private static final Logger logger = LoggerFactory.getLogger(PulsarMQCanalConnector.class); private static final Logger logger = LoggerFactory
.getLogger(PulsarMQCanalConnector.class);
private volatile Messages<byte[]> lastGetBatchMessage; private volatile Messages<byte[]> lastGetBatchMessage;
/** /**
* 连接pulsar客户端 * 连接pulsar客户端
*/ */
private PulsarClient pulsarClient; private PulsarClient pulsarClient;
/** /**
* 消费者 * 消费者
*/ */
private Consumer<byte[]> consumer; private Consumer<byte[]> consumer;
/** /**
* 是否扁平化Canal消息内容 * 是否扁平化Canal消息内容
*/ */
private boolean isFlatMessage = false; private boolean isFlatMessage = false;
/** /**
* 主题名称 * 主题名称
*/ */
private String topic; private String topic;
/** /**
* 环境连接URL * 环境连接URL
*/ */
private String serviceUrl; private String serviceUrl;
/** /**
* 角色认证token * 角色认证token
*/ */
private String roleToken; private String roleToken;
/** /**
* listener name * listener name
*/ */
private String listenerName; private String listenerName;
/** /**
* 订阅客户端名称 * 订阅客户端名称
*/ */
private String subscriptName; private String subscriptName;
/** /**
* 每次批量获取数据的最大条目数,默认30 * 每次批量获取数据的最大条目数,默认30
*/ */
private int batchSize = 30; private int batchSize = 30;
/** /**
* 与{@code batchSize}一起决定批量获取的数据大小 * 与{@code batchSize}一起决定批量获取的数据大小 当:
* 当:
* <p> * <p>
* 1. {@code batchSize} 条消息未消费时<br/> * 1. {@code batchSize} 条消息未消费时<br/>
* 2. 距上一次批量消费时间达到{@code batchTimeoutSeconds}秒时 * 2. 距上一次批量消费时间达到{@code batchTimeoutSeconds}秒时
* </p> * </p>
* 任一条件满足,即执行批量消费 * 任一条件满足,即执行批量消费
*/ */
private int getBatchTimeoutSeconds = 30; private int getBatchTimeoutSeconds = 30;
/** /**
* 批量处理消息时,一次批量处理的超时时间秒数 * 批量处理消息时,一次批量处理的超时时间秒数
* <p> * <p>
* 该时间应该根据{@code batchSize}和{@code batchTimeoutSeconds}合理设置 * 该时间应该根据{@code batchSize}和{@code batchTimeoutSeconds}合理设置
* </p> * </p>
*/ */
private int batchProcessTimeoutSeconds = 60; private int batchProcessTimeoutSeconds = 60;
/** /**
* 消费失败后的重试秒数,默认60秒 * 消费失败后的重试秒数,默认60秒
*/ */
private int redeliveryDelaySeconds = 60; private int redeliveryDelaySeconds = 60;
/** /**
* 当客户端接收到消息,30秒还没有返回ack给服务端时,ack超时,会重新消费该消息 * 当客户端接收到消息,30秒还没有返回ack给服务端时,ack超时,会重新消费该消息
*/ */
private int ackTimeoutSeconds = 30; private int ackTimeoutSeconds = 30;
/** /**
* 是否开启消息失败重试功能,默认开启 * 是否开启消息失败重试功能,默认开启
*/ */
private boolean isRetry = true; private boolean isRetry = true;
/** /**
* <p> * <p>
* true重试(-RETRY)和死信队列(-DLQ)后缀为大写,有些地方创建的为小写,需确保正确 * true重试(-RETRY)和死信队列(-DLQ)后缀为大写,有些地方创建的为小写,需确保正确
* </p> * </p>
*/ */
private boolean isRetryDLQUpperCase = false; private boolean isRetryDLQUpperCase = false;
/** /**
* 最大重试次数 * 最大重试次数
*/ */
private int maxRedeliveryCount = 128; private int maxRedeliveryCount = 128;
/** /**
* 连接标识位,在连接或关闭连接后改变值 * 连接标识位,在连接或关闭连接后改变值
*/ */
private boolean connected = false; private boolean connected = false;
/** /**
* 除必要参数外,其他参数使用默认值 * 除必要参数外,其他参数使用默认值
@@ -125,21 +126,21 @@ public class PulsarMQCanalConnector implements CanalMQConnector {
* </p> * </p>
* *
* @param isFlatMessage true使用扁平消息 * @param isFlatMessage true使用扁平消息
* @param serviceUrl pulsar服务连接地址,通常为:pulsar:host:ip或http://host:ip * @param serviceUrl pulsar服务连接地址,通常为:pulsar:host:ip或http://host:ip
* @param roleToken 有对应topic的消费者权限的角色token * @param roleToken 有对应topic的消费者权限的角色token
* @param topic 订阅主题 * @param topic 订阅主题
* @param subscriptName 订阅和客户端名称,同一个订阅名视为同一个消费实例 * @param subscriptName 订阅和客户端名称,同一个订阅名视为同一个消费实例
* @date 2021/9/18 08:54 * @date 2021/9/18 08:54
* @author chad * @author chad
* @since 1 by chad at 2021/9/18 完善 * @since 1 by chad at 2021/9/18 完善
*/ */
public PulsarMQCanalConnector(boolean isFlatMessage, String serviceUrl, String roleToken, String topic public PulsarMQCanalConnector(boolean isFlatMessage, String serviceUrl, String roleToken, String topic,
, String subscriptName) { String subscriptName){
this(isFlatMessage, serviceUrl, roleToken, topic, subscriptName,null); this(isFlatMessage, serviceUrl, roleToken, topic, subscriptName, null);
} }
public PulsarMQCanalConnector(boolean isFlatMessage, String serviceUrl, String roleToken, String topic public PulsarMQCanalConnector(boolean isFlatMessage, String serviceUrl, String roleToken, String topic,
, String subscriptName, String listenerName) { String subscriptName, String listenerName){
this.isFlatMessage = isFlatMessage; this.isFlatMessage = isFlatMessage;
this.serviceUrl = serviceUrl; this.serviceUrl = serviceUrl;
this.roleToken = roleToken; this.roleToken = roleToken;
@@ -158,19 +159,31 @@ public class PulsarMQCanalConnector implements CanalMQConnector {
* @author chad * @author chad
* @since 1 by chad at 2021/9/18 完善 * @since 1 by chad at 2021/9/18 完善
*/ */
public PulsarMQCanalConnector(boolean isFlatMessage, String serviceUrl, String roleToken, String topic public PulsarMQCanalConnector(boolean isFlatMessage, String serviceUrl, String roleToken, String topic,
, String subscriptName, int batchSize, int getBatchTimeoutSeconds, int batchProcessTimeoutSeconds String subscriptName, int batchSize, int getBatchTimeoutSeconds,
, int redeliveryDelaySeconds, int ackTimeoutSeconds, boolean isRetry, boolean isRetryDLQUpperCase int batchProcessTimeoutSeconds, int redeliveryDelaySeconds, int ackTimeoutSeconds,
, int maxRedeliveryCount) { boolean isRetry, boolean isRetryDLQUpperCase, int maxRedeliveryCount){
this(isFlatMessage, serviceUrl, roleToken, topic, subscriptName, batchSize, getBatchTimeoutSeconds this(isFlatMessage,
, batchProcessTimeoutSeconds, redeliveryDelaySeconds, ackTimeoutSeconds, isRetry, isRetryDLQUpperCase serviceUrl,
, maxRedeliveryCount, null); roleToken,
topic,
subscriptName,
batchSize,
getBatchTimeoutSeconds,
batchProcessTimeoutSeconds,
redeliveryDelaySeconds,
ackTimeoutSeconds,
isRetry,
isRetryDLQUpperCase,
maxRedeliveryCount,
null);
} }
public PulsarMQCanalConnector(boolean isFlatMessage, String serviceUrl, String roleToken, String topic public PulsarMQCanalConnector(boolean isFlatMessage, String serviceUrl, String roleToken, String topic,
, String subscriptName, int batchSize, int getBatchTimeoutSeconds, int batchProcessTimeoutSeconds String subscriptName, int batchSize, int getBatchTimeoutSeconds,
, int redeliveryDelaySeconds, int ackTimeoutSeconds, boolean isRetry, boolean isRetryDLQUpperCase int batchProcessTimeoutSeconds, int redeliveryDelaySeconds, int ackTimeoutSeconds,
, int maxRedeliveryCount, String listenerName) { boolean isRetry, boolean isRetryDLQUpperCase, int maxRedeliveryCount,
String listenerName){
this.isFlatMessage = isFlatMessage; this.isFlatMessage = isFlatMessage;
this.serviceUrl = serviceUrl; this.serviceUrl = serviceUrl;
this.roleToken = roleToken; this.roleToken = roleToken;
@@ -195,13 +208,12 @@ public class PulsarMQCanalConnector implements CanalMQConnector {
// 连接创建客户端 // 连接创建客户端
try { try {
ClientBuilder builder = PulsarClient.builder() ClientBuilder builder = PulsarClient.builder()
.serviceUrl(serviceUrl) .serviceUrl(serviceUrl)
.authentication(AuthenticationFactory.token(roleToken)); .authentication(AuthenticationFactory.token(roleToken));
if (StringUtils.isNotEmpty(listenerName)) { if (StringUtils.isNotEmpty(listenerName)) {
builder.listenerName(listenerName); builder.listenerName(listenerName);
} }
pulsarClient = builder pulsarClient = builder.build();
.build();
} catch (PulsarClientException e) { } catch (PulsarClientException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -250,15 +262,14 @@ public class PulsarMQCanalConnector implements CanalMQConnector {
builder.subscriptionType(SubscriptionType.Failover); builder.subscriptionType(SubscriptionType.Failover);
builder builder
// 调用consumer.negativeAcknowledge(message) (即nack)来表示消费失败的消息 // 调用consumer.negativeAcknowledge(message) (即nack)来表示消费失败的消息
// 在指定的时间进行重新消费,默认是1分钟。 // 在指定的时间进行重新消费,默认是1分钟。
.negativeAckRedeliveryDelay(this.redeliveryDelaySeconds, TimeUnit.SECONDS) .negativeAckRedeliveryDelay(this.redeliveryDelaySeconds, TimeUnit.SECONDS)
.subscriptionName(this.subscriptName) .subscriptionName(this.subscriptName);
;
if (this.isRetry) { if (this.isRetry) {
DeadLetterPolicy.DeadLetterPolicyBuilder dlqBuilder = DeadLetterPolicy.builder() DeadLetterPolicy.DeadLetterPolicyBuilder dlqBuilder = DeadLetterPolicy.builder()
// 最大重试次数 // 最大重试次数
.maxRedeliverCount(this.maxRedeliveryCount); .maxRedeliverCount(this.maxRedeliveryCount);
// 指定重试队列,不是多个或通配符topic才能判断重试队列 // 指定重试队列,不是多个或通配符topic才能判断重试队列
if (!MQUtil.isPatternTag(this.topic)) { if (!MQUtil.isPatternTag(this.topic)) {
String retryTopic = this.topic + (this.isRetryDLQUpperCase ? "-RETRY" : "-retry"); String retryTopic = this.topic + (this.isRetryDLQUpperCase ? "-RETRY" : "-retry");
@@ -267,19 +278,17 @@ public class PulsarMQCanalConnector implements CanalMQConnector {
dlqBuilder.deadLetterTopic(dlqTopic); dlqBuilder.deadLetterTopic(dlqTopic);
} }
//默认关闭,如果需要重试则开启 // 默认关闭,如果需要重试则开启
builder.enableRetry(true) builder.enableRetry(true).deadLetterPolicy(dlqBuilder.build());
.deadLetterPolicy(dlqBuilder.build());
} }
// ack超时 // ack超时
builder.ackTimeout(this.ackTimeoutSeconds, TimeUnit.SECONDS); builder.ackTimeout(this.ackTimeoutSeconds, TimeUnit.SECONDS);
// pulsar批量获取消息设置 // pulsar批量获取消息设置
builder.batchReceivePolicy(new BatchReceivePolicy.Builder() builder.batchReceivePolicy(new BatchReceivePolicy.Builder().maxNumMessages(this.batchSize)
.maxNumMessages(this.batchSize) .timeout(this.getBatchTimeoutSeconds, TimeUnit.SECONDS)
.timeout(this.getBatchTimeoutSeconds, TimeUnit.SECONDS) .build());
.build());
try { try {
this.consumer = builder.subscribe(); this.consumer = builder.subscribe();
@@ -289,7 +298,6 @@ public class PulsarMQCanalConnector implements CanalMQConnector {
} }
} }
@Override @Override
public void subscribe() throws CanalClientException { public void subscribe() throws CanalClientException {
this.subscribe(null); this.subscribe(null);
@@ -310,7 +318,7 @@ public class PulsarMQCanalConnector implements CanalMQConnector {
* 不关注业务执行结果,只要收到消息即认识消费成功,自动ack * 不关注业务执行结果,只要收到消息即认识消费成功,自动ack
* *
* @param timeout 阻塞获取消息的超时时间 * @param timeout 阻塞获取消息的超时时间
* @param unit 时间单位 * @param unit 时间单位
* @return java.util.List<com.alibaba.otter.canal.protocol.Message> * @return java.util.List<com.alibaba.otter.canal.protocol.Message>
* @date 2021/9/13 22:24 * @date 2021/9/13 22:24
* @author chad * @author chad
@@ -326,13 +334,14 @@ public class PulsarMQCanalConnector implements CanalMQConnector {
} }
/** /**
* 关心业务执行结果,业务侧根据执行结果调用 {@link PulsarMQCanalConnector#ack()}或{@link PulsarMQCanalConnector#rollback()} * 关心业务执行结果,业务侧根据执行结果调用
* {@link PulsarMQCanalConnector#ack()}或{@link PulsarMQCanalConnector#rollback()}
* <p> * <p>
* 本方法示支持多线程,在MQ保障顺序的前提下,也无法提供单Topic多线程 * 本方法示支持多线程,在MQ保障顺序的前提下,也无法提供单Topic多线程
* </p> * </p>
* *
* @param timeout 阻塞获取消息的超时时间 * @param timeout 阻塞获取消息的超时时间
* @param unit 时间单位 * @param unit 时间单位
* @return java.util.List<com.alibaba.otter.canal.protocol.Message> * @return java.util.List<com.alibaba.otter.canal.protocol.Message>
* @date 2021/9/13 22:26 * @date 2021/9/13 22:26
* @author chad * @author chad
@@ -366,7 +375,8 @@ public class PulsarMQCanalConnector implements CanalMQConnector {
* @return java.util.List<T> * @return java.util.List<T>
* @date 2021/9/14 15:20 * @date 2021/9/14 15:20
* @author chad * @author chad
* @since 1 by chad at 2021/9/14 供{@link PulsarMQCanalConnector#getListWithoutAck(Long, TimeUnit)} * @since 1 by chad at 2021/9/14
* 供{@link PulsarMQCanalConnector#getListWithoutAck(Long, TimeUnit)}
* 和{@link PulsarMQCanalConnector#getFlatListWithoutAck(Long, TimeUnit)}调用 * 和{@link PulsarMQCanalConnector#getFlatListWithoutAck(Long, TimeUnit)}调用
*/ */
private <T> List<T> getListWithoutAck() { private <T> List<T> getListWithoutAck() {

View File

@@ -64,6 +64,6 @@ public class PulsarMQConstants {
/** /**
* Pulsar admin服务器地址 * Pulsar admin服务器地址
*/ */
public static final String PULSARMQ_LISTENER_NAME = ROOT + "." + "listenerName"; public static final String PULSARMQ_LISTENER_NAME = ROOT + "." + "listenerName";
} }

View File

@@ -56,7 +56,7 @@ public class CanalPulsarMQConsumer implements CanalMsgConsumer {
private String roleToken; private String roleToken;
/** /**
* listener name * listener name
*/ */
private String listenerName; private String listenerName;

View File

@@ -74,7 +74,7 @@ public class CanalPulsarMQProducer extends AbstractMQProducer implements CanalMQ
builder.authentication(AuthenticationFactory.token(pulsarMQProducerConfig.getRoleToken())); builder.authentication(AuthenticationFactory.token(pulsarMQProducerConfig.getRoleToken()));
} }
if (StringUtils.isNotEmpty(pulsarMQProducerConfig.getListenerName())) { if (StringUtils.isNotEmpty(pulsarMQProducerConfig.getListenerName())) {
//listener name // listener name
builder.listenerName(pulsarMQProducerConfig.getListenerName()); builder.listenerName(pulsarMQProducerConfig.getListenerName());
} }

View File

@@ -1,10 +1,8 @@
package com.taobao.tddl.dbsync.binlog; package com.taobao.tddl.dbsync.binlog;
import java.nio.charset.Charset; import static com.taobao.tddl.dbsync.binlog.event.RowsLogBuffer.*;
import static com.taobao.tddl.dbsync.binlog.event.RowsLogBuffer.appendNumber2; import java.nio.charset.Charset;
import static com.taobao.tddl.dbsync.binlog.event.RowsLogBuffer.appendNumber4;
import static com.taobao.tddl.dbsync.binlog.event.RowsLogBuffer.usecondsToStr;
/** /**
* 处理下MySQL json二进制转化为可读的字符串 * 处理下MySQL json二进制转化为可读的字符串
@@ -34,26 +32,25 @@ public class JsonConversion {
public static final char JSONB_FALSE_LITERAL = '\2'; public static final char JSONB_FALSE_LITERAL = '\2';
/* /*
* The size of offset or size fields in the small and the large storage * The size of offset or size fields in the small and the large storage format
* format for JSON objects and JSON arrays. * for JSON objects and JSON arrays.
*/ */
public static final int SMALL_OFFSET_SIZE = 2; public static final int SMALL_OFFSET_SIZE = 2;
public static final int LARGE_OFFSET_SIZE = 4; public static final int LARGE_OFFSET_SIZE = 4;
/* /*
* The size of key entries for objects when using the small storage format * The size of key entries for objects when using the small storage format or
* or the large storage format. In the small format it is 4 bytes (2 bytes * the large storage format. In the small format it is 4 bytes (2 bytes for key
* for key length and 2 bytes for key offset). In the large format it is 6 * length and 2 bytes for key offset). In the large format it is 6 (2 bytes for
* (2 bytes for length, 4 bytes for offset). * length, 4 bytes for offset).
*/ */
public static final int KEY_ENTRY_SIZE_SMALL = (2 + SMALL_OFFSET_SIZE); public static final int KEY_ENTRY_SIZE_SMALL = (2 + SMALL_OFFSET_SIZE);
public static final int KEY_ENTRY_SIZE_LARGE = (2 + LARGE_OFFSET_SIZE); public static final int KEY_ENTRY_SIZE_LARGE = (2 + LARGE_OFFSET_SIZE);
/* /*
* The size of value entries for objects or arrays. When using the small * The size of value entries for objects or arrays. When using the small storage
* storage format, the entry size is 3 (1 byte for type, 2 bytes for * format, the entry size is 3 (1 byte for type, 2 bytes for offset). When using
* offset). When using the large storage format, it is 5 (1 byte for type, 4 * the large storage format, it is 5 (1 byte for type, 4 bytes for offset).
* bytes for offset).
*/ */
public static final int VALUE_ENTRY_SIZE_SMALL = (1 + SMALL_OFFSET_SIZE); public static final int VALUE_ENTRY_SIZE_SMALL = (1 + SMALL_OFFSET_SIZE);
public static final int VALUE_ENTRY_SIZE_LARGE = (1 + LARGE_OFFSET_SIZE); public static final int VALUE_ENTRY_SIZE_LARGE = (1 + LARGE_OFFSET_SIZE);
@@ -160,13 +157,12 @@ public class JsonConversion {
if (len < n + str_len) { if (len < n + str_len) {
throw new IllegalArgumentException("illegal json data"); throw new IllegalArgumentException("illegal json data");
} }
return new Json_Value(Json_enum_type.STRING, buffer.rewind() return new Json_Value(Json_enum_type.STRING,
.forward((int) n) buffer.rewind().forward((int) n).getFixString((int) str_len, charset));
.getFixString((int) str_len, charset));
case JSONB_TYPE_OPAQUE: case JSONB_TYPE_OPAQUE:
/* /*
* There should always be at least one byte, which tells the * There should always be at least one byte, which tells the field type of the
* field type of the opaque value. * opaque value.
*/ */
// The type is encoded as a uint8 that maps to an // The type is encoded as a uint8 that maps to an
// enum_field_types. // enum_field_types.
@@ -368,45 +364,45 @@ public class JsonConversion {
buf.append('"').append(text).append('"'); buf.append('"').append(text).append('"');
} else if (m_field_type == LogEvent.MYSQL_TYPE_DATE || m_field_type == LogEvent.MYSQL_TYPE_DATETIME } else if (m_field_type == LogEvent.MYSQL_TYPE_DATE || m_field_type == LogEvent.MYSQL_TYPE_DATETIME
|| m_field_type == LogEvent.MYSQL_TYPE_TIMESTAMP) { || m_field_type == LogEvent.MYSQL_TYPE_TIMESTAMP) {
long packed_value = m_data.getLong64(); long packed_value = m_data.getLong64();
if (packed_value == 0) { if (packed_value == 0) {
text = "0000-00-00 00:00:00"; text = "0000-00-00 00:00:00";
} else { } else {
// 构造TimeStamp只处理到秒 // 构造TimeStamp只处理到秒
long ultime = Math.abs(packed_value); long ultime = Math.abs(packed_value);
long intpart = ultime >> 24; long intpart = ultime >> 24;
int frac = (int) (ultime % (1L << 24)); int frac = (int) (ultime % (1L << 24));
long ymd = intpart >> 17; long ymd = intpart >> 17;
long ym = ymd >> 5; long ym = ymd >> 5;
long hms = intpart % (1 << 17); long hms = intpart % (1 << 17);
// text = // text =
// String.format("%04d-%02d-%02d %02d:%02d:%02d", // String.format("%04d-%02d-%02d %02d:%02d:%02d",
// (int) (ym / 13), // (int) (ym / 13),
// (int) (ym % 13), // (int) (ym % 13),
// (int) (ymd % (1 << 5)), // (int) (ymd % (1 << 5)),
// (int) (hms >> 12), // (int) (hms >> 12),
// (int) ((hms >> 6) % (1 << 6)), // (int) ((hms >> 6) % (1 << 6)),
// (int) (hms % (1 << 6))); // (int) (hms % (1 << 6)));
StringBuilder builder = new StringBuilder(26); StringBuilder builder = new StringBuilder(26);
appendNumber4(builder, (int) (ym / 13)); appendNumber4(builder, (int) (ym / 13));
builder.append('-'); builder.append('-');
appendNumber2(builder, (int) (ym % 13)); appendNumber2(builder, (int) (ym % 13));
builder.append('-'); builder.append('-');
appendNumber2(builder, (int) (ymd % (1 << 5))); appendNumber2(builder, (int) (ymd % (1 << 5)));
builder.append(' '); builder.append(' ');
appendNumber2(builder, (int) (hms >> 12)); appendNumber2(builder, (int) (hms >> 12));
builder.append(':'); builder.append(':');
appendNumber2(builder, (int) ((hms >> 6) % (1 << 6))); appendNumber2(builder, (int) ((hms >> 6) % (1 << 6)));
builder.append(':'); builder.append(':');
appendNumber2(builder, (int) (hms % (1 << 6))); appendNumber2(builder, (int) (hms % (1 << 6)));
builder.append('.').append(usecondsToStr(frac, 6)); builder.append('.').append(usecondsToStr(frac, 6));
text = builder.toString(); text = builder.toString();
} }
buf.append('"').append(text).append('"'); buf.append('"').append(text).append('"');
} else { } else {
text = m_data.getFixString((int) m_length, charset); text = m_data.getFixString((int) m_length, charset);
buf.append('"').append(escapse(text)).append('"'); buf.append('"').append(escapse(text)).append('"');
} }
break; break;
case STRING: case STRING:
@@ -452,7 +448,8 @@ public class JsonConversion {
} }
public static enum Json_enum_type { public static enum Json_enum_type {
OBJECT, ARRAY, STRING, INT, UINT, DOUBLE, LITERAL_NULL, LITERAL_TRUE, LITERAL_FALSE, OPAQUE, ERROR OBJECT, ARRAY, STRING, INT, UINT, DOUBLE, LITERAL_NULL, LITERAL_TRUE,
LITERAL_FALSE, OPAQUE, ERROR
} }
} }

View File

@@ -1,5 +1,7 @@
package com.taobao.tddl.dbsync.binlog; package com.taobao.tddl.dbsync.binlog;
import java.nio.charset.Charset;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@@ -9,8 +11,6 @@ import org.junit.rules.Timeout;
import com.taobao.tddl.dbsync.binlog.JsonConversion.Json_Value; import com.taobao.tddl.dbsync.binlog.JsonConversion.Json_Value;
import com.taobao.tddl.dbsync.binlog.JsonConversion.Json_enum_type; import com.taobao.tddl.dbsync.binlog.JsonConversion.Json_enum_type;
import java.nio.charset.Charset;
public class JsonConversion_Json_ValueTest { public class JsonConversion_Json_ValueTest {
@Rule @Rule
@@ -50,7 +50,6 @@ public class JsonConversion_Json_ValueTest {
// Act // Act
thrown.expect(IllegalArgumentException.class); thrown.expect(IllegalArgumentException.class);
JsonConversion.parse_value(type, buffer, len, charsetName); JsonConversion.parse_value(type, buffer, len, charsetName);
// Method is not expected to return due to exception thrown // Method is not expected to return due to exception thrown
} }
@@ -60,7 +59,10 @@ public class JsonConversion_Json_ValueTest {
// {"internal_uri_rewrite": {"(.*)(/[^/]+\\.(mp|MP)4)$": "$1/mp4$2"}} // {"internal_uri_rewrite": {"(.*)(/[^/]+\\.(mp|MP)4)$": "$1/mp4$2"}}
String jsonData = "{\"internal_uri_rewrite\": {\"(.*)(/[^/]+\\\\.(mp|MP)4)$\": \"$1/mp4$2\"}}"; String jsonData = "{\"internal_uri_rewrite\": {\"(.*)(/[^/]+\\\\.(mp|MP)4)$\": \"$1/mp4$2\"}}";
byte[] data = new byte[]{1, 0, 74, 0, 11, 0, 20, 0, 0, 31, 0, 105, 110, 116, 101, 114, 110, 97, 108, 95, 117, 114, 105, 95, 114, 101, 119, 114, 105, 116, 101, 1, 0, 43, 0, 11, 0, 23, 0, 12, 34, 0, 40, 46, 42, 41, 40, 47, 91, 94, 47, 93, 43, 92, 46, 40, 109, 112, 124, 77, 80, 41, 52, 41, 36, 8, 36, 49, 47, 109, 112, 52, 36, 50}; byte[] data = new byte[] { 1, 0, 74, 0, 11, 0, 20, 0, 0, 31, 0, 105, 110, 116, 101, 114, 110, 97, 108, 95, 117,
114, 105, 95, 114, 101, 119, 114, 105, 116, 101, 1, 0, 43, 0, 11, 0, 23, 0, 12, 34,
0, 40, 46, 42, 41, 40, 47, 91, 94, 47, 93, 43, 92, 46, 40, 109, 112, 124, 77, 80, 41,
52, 41, 36, 8, 36, 49, 47, 109, 112, 52, 36, 50 };
final LogBuffer buffer = new LogBuffer(data, 0, 74); final LogBuffer buffer = new LogBuffer(data, 0, 74);
Charset charset = Charset.forName("UTF-8"); Charset charset = Charset.forName("UTF-8");
@@ -68,7 +70,6 @@ public class JsonConversion_Json_ValueTest {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
jsonValue.toJsonString(builder, charset); jsonValue.toJsonString(builder, charset);
Assert.assertEquals(builder.toString(), jsonData); Assert.assertEquals(builder.toString(), jsonData);
Assert.assertEquals(jsonValue.key(0, charset), "internal_uri_rewrite"); Assert.assertEquals(jsonValue.key(0, charset), "internal_uri_rewrite");