【问题标题】:send and receive Date type with Avro on java在 java 上使用 Avro 发送和接收日期类型
【发布时间】:2018-03-02 19:34:19
【问题描述】:

我可以用Apache Avro 发送和接收日期类型吗?我无法找到任何关于此的内容。只有我发现的东西说在模式中使用日期的 int 和logicalType。但这会在接收方产生另一个 int 。我仍然需要将其转换为日期。

我正在尝试从Apache Kafka 生产者发送日期并在 Kafka 消费者中接收。

如果没有其他方法,那么我是否必须始终将日期转换为 int,然后再返回给消费者。有这篇文章展示了如何做到这一点:

Get the number of days, weeks, and months, since Epoch in Java

序列化代码:-

@Override
    public byte[] serialize(String topic, T data) {
        try {
            byte[] result = null;

            if (data != null) {
                logger.debug("data='{}'" +  data);

                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                BinaryEncoder binaryEncoder =
                        EncoderFactory.get().binaryEncoder(byteArrayOutputStream, null);

                DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<>(data.getSchema());
                datumWriter.write(data, binaryEncoder);

                binaryEncoder.flush();
                byteArrayOutputStream.close();

                result = byteArrayOutputStream.toByteArray();
                byteArrayOutputStream.close();
                logger.debug("serialized data='{}'" +  DatatypeConverter.printHexBinary(result));
            }
            return result;
        } catch (IOException ex) {
            throw new SerializationException(
                    "Can't serialize data='" + data + "' for topic='" + topic + "'", ex);
        }
    }

解串器代码:-

    @Override
    public T deserialize(String topic, byte[] data) {
        try {
            T result = null;

            if (data != null) {
                logger.debug("data='{}'" + DatatypeConverter.printHexBinary(data));

                DatumReader<GenericRecord> datumReader =
                        new SpecificDatumReader<>(targetType.newInstance().getSchema());
                Decoder decoder = DecoderFactory.get().binaryDecoder(data, null);

                result = (T) datumReader.read(null, decoder);
                logger.debug("deserialized data='{}'" + result);                
            }
            return result;
        } catch (Exception ex) {
            throw new SerializationException(
                    "Can't deserialize data '" + Arrays.toString(data) + "' from topic '" + topic + "'", ex);
        }
    }

架构文件:-

{"namespace": "com.test",
  "type": "record",
  "name": "Measures",
  "fields": [  
    {"name": "transactionDate", "type": ["int", "null"], "logicalType" : "date" }
   ]
}

这两个只是在生产者和消费者配置中定义为序列化器和反序列化器类。

【问题讨论】:

  • 接收方的int”是什么意思?您反序列化为的 Java 类型应该有一个 Avro 可以填充的 Date 字段。我也强烈建议不要使用Date - 如果您需要一个时间点,请使用Instant
  • 您的架构错误 - 逻辑类型在类型上而不是在字段上。 { "type": "long", "logicalType": "date" }
  • 您有什么理由使用自己的解码器? Confluent 提供了自己的docs.confluent.io/current/schema-registry/docs/…
  • 谢谢@BoristheSpider,我的架构是错误的,在更正它并在阅读下面的 Basil 回复后使用适配器后,我可以让它像 joda LocalDate 一样工作。我想尽可能避免融合,但没有任何理由。

标签: java apache-kafka avro


【解决方案1】:

我没有使用过 Apace Avro 或 Apache Kafka,但也许这会有所帮助……

我可以用 Apache Avro 发送和接收 Date 类型吗

查看维基百科页面,Avro 中没有定义Date 类型:

Avro 模式是使用 JSON 定义的。 Schema 由基本类型(null、boolean、int、long、float、double、bytes 和 string)和复杂类型(record、enum、array、map、union 和 fixed)组成。

JSON also lacks date-time types.

ISO 8601

在这种不提供日期时间支持的情况下,我建议使用标准的ISO 8601 格式将日期时间值序列化为文本。这些格式旨在实用:易于机器解析,易于跨文化的人类阅读,同时避免歧义。

对于仅日期值,格式为 YYYY-MM-DD。 2018 年 1 月 23 日将是 2018-01-23

java.time

java.time 类在解析/生成字符串时默认使用 ISO 8601 格式。

LocalDate 类表示没有时间和时区的仅日期值。

LocalDate.of( 2018 , Month.JANUARY , 23 )
         .toString()                              // Generating a string in standard format.

2018-01-23

LocalDate ld = LocalDate.parse( "2018-01-23" ) ;  // Parsing a string in standard format.

从纪元开始计数

我不建议从epoch reference 开始跟踪日期时间值。但是,如果您决定这样做,java.time 类可以提供帮助。

1970-01-01 的纪元参考日期定义为常量LocalDate.EPOCH

获取自该纪元参考以来的天数。

long daysSinceEpoch = ld.toEpochDay() ;

17554

解析自纪元以来的天数。将 17,554 天添加到 1970-01-01 会得到 2018-01-23。

LocalDate ld = LocalDate.ofEpochDay( 17_554L ) ;  // 1970-01-01 + 17,554 days = 2018-01-23

你可以明白为什么我不推荐这种从纪元开始计数的方法:阅读和调试2018-01-23 比破译17554 容易得多。

乔达时间

Apache Avro 包含一个 adapter class for Joda-Time 类型 (ticket AVRO-1672)。我不知道是否为 java.time 类型构建了这样的适配器。

Joda-Time 项目是 Java 内置的 java.time 框架的前身。该项目现在处于维护模式,作者建议迁移到 java.time 类。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

  • 不太正确 - Avro 'logicalType' 在 Java 类型和有线协议之间添加了一个转换层。它基本上说“这个long是一个日期”。
  • 它确实这么说,这就是文档所说的,但是你如何建议它反序列化它到日期,它仍然会映射到与 long 相同的长字段,因此需要另一个转换来获取日期没多久?
  • Avro 为您进行转换@AtulOjha - 架构告诉它它需要。我不清楚您为什么/如何访问 Avro 协议中的底层数据。
  • 我有一个字段 transactionDate。我使用 joda 日期时间库将日期转换为自 Kafka 生产者端的纪元以来的天数,然后在消费者端执行 getTransactionDate() 时,我得到相同的数字(从纪元开始的天数而不是日期)。在模式文件中,它被定义为:- {"name": "transactionDate", "type": ["int", "null"], "logicalType" : "date" }
  • @AtulOjha 你找到解决方案了吗?我面临同样的情况,从 Avro 模式自动生成 dto,getter 字段返回一个 int 而不是 Date。
猜你喜欢
  • 2018-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-10
  • 2019-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多