转载---Flink 如何将聚合结果写入kafka ——读写canal-json

概述

  • 1.11版本最重要的特性Hive Streaming在之前的博客,包括Flink中文社区的微信公众号上,都和大家聊过不少次了。那么除此之外,还有什么特性值得大家关注呢?
    • CDC数据的解析:可以解析canal、debezium推到kafka中的binlog;如果在binlog中是个DELETE的操作,那么会自动解析成撤回流,将之前那条下发过的数据撤回;美中不足的是,只支持读,不支持写,这也就以为着无法将撤回流写入kafka
    • Postgres Catalog:如果要用Postgres中的表,不需要再CREATE TABLE了,直接用就好了,就像整合了Hive之后,用Hive中的表一样简单;美中不足的是,只支持使用已有的表,没法把表建在Postgres Catalog中
  • 今天我们要聊的,就是CDC中的canal-json;并且,我自己实现了将计算完的数据,再解析成canal-json,这也就意味着,可以将聚合过的结果插入kafka(1.10版本中,想将聚合结果插入kafka可以参考这个文章聚合结果写入Kafka
  • 二话不说,我们开始吧,先从canal-json source开始

canal-json source

  • 先建一下表

    drop table if exists topic_products;
    CREATE TABLE topic_products (
      -- schema is totally the same to the MySQL "products" table
      id BIGINT,
      name STRING,
      description STRING,
      weight DECIMAL(10, 2)
    ) WITH (
     'connector' = 'kafka',
     'topic' = 'products_binlog',
     'properties.bootstrap.servers' = '10.70.98.1:9092',
     'properties.group.id' = 'testGroup',
     'scan.startup.mode' = 'earliest-offset',
     'format' = 'canal-json'  -- using canal-json as the format
    )
    
    drop table if exists print_table;
    CREATE TABLE print_table WITH ('connector' = 'print')
    LIKE topic_products (EXCLUDING ALL)
    
  • 这里之所以用print这个新的connector,一方面是因为Zeppelin展示的时候看不到撤回的效果,一方面考虑到有些同学可能用的是tEnv.executeSql(sql).print,在接收撤回流时这个语句会报错,所以正好用上了这个新的connector

  • 用Kafka命令行插入两条数据到Topic中

    {"data":[{"id":"4","name":"新增测试","description":"这是测试","weight":"100.1"}],"database":"test","es":1595487446000,"id":2,"isDdl":false,"mysqlType":{"id":"integer(255)","name":"varchar(255)","description":"varchar(255)","weight":"float"},"old":null,"pkNames":["id"],"sql":"","sqlType":{"id":4,"name":12,"description":12,"weight":7},"table":"products","ts":1595487446183,"type":"INSERT"}
    
    {"data":[{"id":"4","name":"新增测试(修改)","description":"这是测试进行了修改","weight":"100.1"}],"database":"test","es":1595487509000,"id":3,"isDdl":false,"mysqlType":{"id":"integer(255)","name":"varchar(255)","description":"varchar(255)","weight":"float"},"old":[{"name":"新增测试","description":"这是测试"}],"pkNames":["id"],"sql":"","sqlType":{"id":4,"name":12,"description":12,"weight":7},"table":"products","ts":1595487509389,"type":"UPDATE"}
    
  • 再让我们到flink web页面上看看我们print的结果
    1

  • 大家可能会很奇怪,为啥子出现了三条结果;大家可以看一下CanalJsonDeserializationSchema这个类的deserialize(byte[] message, Collector<RowData> out)方法

  • 它会将UPDATE类型的canal-json解析成两条数据,json中的old数据代表修改之前的数据,标记为UPDATE_BEFORE类型;data代表是修改之后的数据,标记为UPDATE_AFTER类型;将olddata都下发,引擎会根据类型去判断这条数据是撤回还是下发。

canal-json sink

  • 说完了canal-json source,我们来聊聊我们今天的重点,canal-json sink
  • 之前我们在讲聚合结果写入kafka的时候,说过一个缺点:因为Kafka只支持追加插入操作,不支持更新和删除操作,所以同样的Key有多条记录,我们需要在下游任务进行对数据的去重。这样会给下游使用这个topic的同学带来疑惑:为啥还要去重?聚合结果不应该是一条吗?这怎么和离线不一样?同样的,去重也会带来一定的性能损耗。
  • 那么,有更好的做法吗?答:有的,用canal-json。遗憾的是,社区还未实现,直接使用的话会抛出异常Canal format doesn't support as a sink format yet.。不过我自己先简单的实现了一下,时间原因+工作繁忙,所以没有做太完善的测试,包括有些地方写的也不够优雅,不过大家可以自己再补充一下
  • canal-json sink会将你的数据重新包装成canal-json的格式,只保留三个字段(因为canal-json source也只需要三个字段)data old type
  • 大概说一下我的处理方式吧,聚合数据写到kafka会有多种类型(在flink中的类型,不是指canal-json中的type
    • INSERT:没啥特殊的,直接序列化插入好了
    • UPDATE_BEFOREUPDATE_AFTER:按照canal-json的field定义,是要放在同一条数据中的;UPDATE_BEFORE对应的是canal-json中的old字段,UPDATE_AFTER对应的是data。但是我发现存在同一条字段中会有一些问题:怎么将beforeafter对应起来呢?也就是说key是啥?第二个问题:这样搞得话需要将before先放在内存里面缓存,那么很容易内存就炸了。然后我又参考了一下阿里云实时计算平台文档
      2

所以我决定将UPDATE_BEFORE解析为DELETE,也就是将canal-json中的type设置为DELETEold为空,数据放到data中;UPDATE_AFTER解析为INSERT,也就是将canal-json中的type设置为INSERTold一样为空,数据放到data中。
DELETE:和UPDATE_BEFORE的处理方式一样

  • 然后将包装完的json再序列化发送到kafka

  • 原理上的东西说完了,具体大家代码大家可以点这里,然后替换源码中的类再编译就好了,也可以直接点我下载jar包替换

  • 下面给大家演示一下吧
    3

  • 可以看到,我是将kafka中csv结构的数据,完成了一次聚合又写入了canal-json格式的kafka中,然后又从中查询再展示

  • 原始数据
    4

  • 插入print connector中的数据。很明显的能看到,不正确的数据有被撤回的标记-D
    5

  • 最后再给大家看一下,kafka中的canal-json格式数据,也和我们预期的一样,很完美
    6

写在最后

  • 时间仓促,没有完整的测试,包括单测也没有写,但是基本的功能也有了可以先测试用用
  • CDC的出现终于摆脱了无法将聚合结果写入kafka的困境,另外社区出的flink-cdc-connectors让我们看到了flink可以做的另一件事情:同步binlog。感觉在一定程度上可以替代canal这个中间件
  • 下一期会和大家聊一下自定义mysql catalog,不过我还没开始看相关源码,什么时候写出来也就不知道了🤣
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值