解决Canal 同步过程时间转换异常问题

514 阅读2分钟

我们通过MySQL->Canal->ElasticSearch 进行数据同步的。版本分别是MySQL 8.0,Canal 1.1.7和 ElasticSearch 8.13.0 但是配置好了数据同步之后,通过 spring-data-elasticsearch 查询的时候,会出现时间类型无法转换的问题。

但是这样的方式有个问题,就是其中的时间类型,如 saleTime,会出现字段映射失败的情况,报错内容如下:

java.lang.RuntimeException: org.springframework.data.elasticsearch.core.convert.ConversionException:
 Unable to convert value '2024-05-13T16:04:16+08:00' to java.util.Date for property 'saleTime'org.springframework.data.elasticsearch.core.convert.ConversionException: 
 Unable to convert value '2024-05-13T16:04:16+08:00' to java.util.Date for property 'saleTime' 
 at org.springframework.data.elasticsearch.core.convert.DatePropertyValueConverter.read(DatePropertyValueConverter.java:56) 
 at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter$Reader.convertOnRead(MappingElasticsearchConverter.java:524) 
 at org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter$Reader.propertyConverterRead(MappingElasticsearchConverter.java:518)

也就是说,日期类型,在 ES 中存储的是'2024-05-13T16:04:16+08:00'这种格式,而在代码中是无法转成我们想要的 Date 类型的。

这里我尝试了很多方法,比如通过设置saleTime的pattern,如:

@Field(name = "sale_time",type = FieldType.Date, format = {},pattern = "yyyy-MM-dd'T'HH:mm:ss'+08:00' ||yyyy-MM-dd||strict_date_optional_time||epoch_millis")private Date saleTime;

也是不行的。从 ES到代码这部分搞不定了,那接下来就想办法让 ES 存储的内容做一下改变吧。如果能让 ES 存储'2024-05-13 16:04:16' 而不是 '2024-05-13T16:04:16+08:00' 就能解决问题了。 这里尝试修改 ES 的索引配置,如:

PUT nfturbo_collection
{
  "mappings": {
    "properties": {
      
      "sale_time": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd'T'HH:mm:ss'+08:00 || strict_date_optional_time || epoch_millis"
      },
     
    }
  }
}

也不行。

那就只剩最后一条路了,那就是在 Canal 上下功夫了,看看 ES 为啥会存 '2024-05-13T16:04:16+08:00' 这样的形式呢,

通过查看 Canal 的源码,可以看到在ESSyncUtil中,针对日期类型,他会使用如下方式进行格式化:

DateTime dateTime = new DateTime(((java.sql.Timestamp) val).getTime());
if (dateTime.getMillisOfSecond() != 0) {
  res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS" + Util.timeZone);
} else {
  res = dateTime.toString("yyyy-MM-dd'T'HH:mm:ss" + Util.timeZone);
}

这也是为啥会出现 '2024-05-13T16:04:16+08:00'这种格式的原因了,因为我们的项目不涉及到多时区,所以直接尝试着修改 Canal 源码。

找到ESSyncUtil中的8处日期转换部分内容,做如下修改:

image.png 修改后重新打包,编译成 jar包。因为我们的ES 是8.0的版本,所以最终到workspace/canal-master/client-adapter/es8x/target下找到client-adapter.es8x-1.1.8-SNAPSHOT-jar-with-dependencies.jar这个编译后的包。

然后把他上传到 Canal 中,上传到Canal 安装目录的 plugin 目录下/root/package/canal-adapter/plugin