Kafka Streams JDBC Source长期不兼容

时间:2018-01-29 15:45:14

标签: apache-kafka apache-kafka-streams apache-kafka-connect confluent-schema-registry

问题:在设置Kafka管道使用带有Avro序列化器和反序列化器的Kafka Connect JDBC源之后,一旦我尝试使用Kafka Streams Java应用程序将该数据读入KStream,我就会收到以下错误。

  

org.apache.kafka.common.errors.SerializationException:数据大小   由LongDeserializer收到的不是8

我试图尽可能地遵循现有的例子,但有些事情是没有意义的。我将在下面提供所有代码/附加信息,但这里有几个问题......

  1. 我目前理解的最大差距之一就是用于" KEY"对于Avro记录?我错误的行(在运行时)与我告诉KStream密钥是LONG的事实有关,但是当检索到Avro记录时,长度小于8(预期长度为LONG)型)。
    当我设置我的JDBC Source时,没有什么可以识别密钥是什么 - 我在文档中没有看到任何可以让我相信我可以指定密钥的东西,尽管我'我试图:

    curl -X POST \
      -H "Content-Type: application/json" \
      --data 'see next code block for formatted data'  \
    http://localhost:8083/connectors
    
    // This is the data chunk used above but in a string - broke it apart for readability here
    {
        "name": "source-jdbc-ldw_applications",
        "config": {
            "connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
            "tasks.max": 1,
            "connection.url": "jdbc:sqlserver://dbserver;databaseName=dbname;user=kafkareader;password=kafkareader;",
            "mode": "incrementing",
            "incrementing.column.name": "ApplicationID",
            "topic.prefix": "source-jdbc-",
            "poll.interval.ms": 30000,
            "table.whitelist": "LDW_Applications",
            "transforms": "setSchema",
            "transforms.setSchema.type": "org.apache.kafka.connect.transforms.SetSchemaMetadata$Value",
            "transforms.setSchema.schema.name": "com.mycompany.avro.Application",
            "transforms.setSchema.schema.version": "1"
        }
    }
    
  2. 通过上面的操作,我得到了以下架构:

    curl http://localhost:8081/subjects/source-jdbc-LDW_Applications-value/versions/1 |jq
    

    以下是其输出:

    {
        "subject": "source-jdbc-LDW_Applications-value",
        "version": 1,
        "id": 9,
        "schema": "{\"type\":\"record\",\"name\":\"Application\",\"namespace\":\"com.baydynamics.avro\",\"fields\":[{\"name\":\"ApplicationID\",\"type\":\"long\"},{\"name\":\"Name\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"Description\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"Group\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"OwnerUserID\",\"type\":[\"null\",\"long\"],\"default\":null},{\"name\":\"RiskScore\",\"type\":[\"null\",{\"type\":\"int\",\"connect.type\":\"int16\"}],\"default\":null},{\"name\":\"RiskRating\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"ServiceLevelTierID\",\"type\":[\"null\",\"int\"],\"default\":null},{\"name\":\"LossPotentialID\",\"type\":[\"null\",\"int\"],\"default\":null},{\"name\":\"ConfidentialityRequirementID\",\"type\":[\"null\",\"int\"],\"default\":null},{\"name\":\"IntegrityRequirementID\",\"type\":[\"null\",\"int\"],\"default\":null},{\"name\":\"AvailabilityRequirementID\",\"type\":[\"null\",\"int\"],\"default\":null},{\"name\":\"ApplicationCategoryID\",\"type\":[\"null\",\"long\"],\"default\":null}],\"connect.version\":1,\"connect.name\":\"com.baydynamics.avro.Application\"}"
    }
    

    要看到那个架构更漂亮一点:

    {
    "type":"record",
    "name":"Application",
    "namespace":"com.baydynamics.avro",
    "fields":[
        {
            "name":"ApplicationID",
            "type":"long"
        },
        {
            "name":"Name",
            "type":[
                "null",
                "string"
            ],
            "default":null
        },
        {
            "name":"Description",
            "type":[
                "null",
                "string"
            ],
            "default":null
        },
        {
            "name":"Group",
            "type":[
                "null",
                "string"
            ],
            "default":null
        },
        {
            "name":"OwnerUserID",
            "type":[
                "null",
                "long"
            ],
            "default":null
        },
        {
            "name":"RiskScore",
            "type":[
                "null",
                {
                "type":"int",
                "connect.type":"int16"
                }
            ],
            "default":null
        },
        {
            "name":"RiskRating",
            "type":[
                "null",
                "string"
            ],
            "default":null
        },
        {
            "name":"ServiceLevelTierID",
            "type":[
                "null",
                "int"
            ],
            "default":null
        },
        {
            "name":"LossPotentialID",
            "type":[
                "null",
                "int"
            ],
            "default":null
        },
        {
            "name":"ConfidentialityRequirementID",
            "type":[
                "null",
                "int"
            ],
            "default":null
        },
        {
            "name":"IntegrityRequirementID",
            "type":[
                "null",
                "int"
            ],
            "default":null
        },
        {
            "name":"AvailabilityRequirementID",
            "type":[
                "null",
                "int"
            ],
            "default":null
        },
        {
            "name":"ApplicationCategoryID",
            "type":[
                "null",
                "long"
            ],
            "default":null
        }
    ],
    "connect.version":1,
    "connect.name":"com.baydynamics.avro.Application"
    }
    

    所以再一次,我在那里看不到任何可能表明任何特定领域的东西将是记录的关键。

    然后我进入Kafka Streams,我尝试将这些数据带入KStream ......它就会炸弹......

    final KStream<Long, Application> applicationStream = builder.stream(Serdes.Long(), applicationSerde, VULNERABILITY_TOPIC);
    

    所以,这就是事情,因为我知道幕后存储的数据是SQL Server中的BIGINT并且映射到Java中的LONG,我将KStream的键类型设置为Long并且然后我使用Serdes.Long()反序列化器作为KStream构建器的参数。

    调试时,我看到原始记录的长度为7,这就是它抛出错误的原因。显然,Avro以一种更好的压缩方式序列化事物?我不知道。无论如何,事情是我甚至不知道它认为它实际使用的是什么键?!那么谁知道 - 也许我对Long的假设不正确,因为它实际上没有使用ApplicationID作为密钥?为什么我甚至认为它是?!

    对此有任何帮助将不胜感激。我知道那里有很多信息,但简而言之......

    1. 使用JDBC Kafka connect将数据推送到主题
    2. 数据正在进入主题 - 我可以通过控制台看到它
    3. 尝试将数据推送到流中,这样我就可以对数据进行一些很棒的事情,并且由于Serdes与Avro Record不兼容,因此它会试图填充流,因为它与Avro Record不兼容
    4. 更新1: 根据下面Randall的建议,我去尝试了SMT(单消息转换),现在每个记录都有一个密钥,这是朝着正确方向迈出的一个很好的一步,但由于某些原因它并没有出现强制转换为Long(INT64)有任何实际效果。我已经使用SMT获取了连接器配置的一些屏幕截图,结果记录(现在有一个密钥!)和我在Kafka流中看到的相同错误: Screenshots mentioned above

1 个答案:

答案 0 :(得分:3)

Confluent JDBC source connector不会生成带密钥的记录。已经记录了添加此支持的feature request

与此同时,您可以使用单个消息转换从值中提取一些字段,从而基本上创建密钥。内置的ValueToKey transform就是这样做的。 This blog post有一个SMT的例子。