Flink Sql教程(6)

FLINK SQL 解析复杂JSON&维表关联HBASE

解析复杂JSON

  • 目前我们的数据源都是以JSON格式存储在KAFKA中,所以今天就写一下怎么在DDL语句中,解析复杂JSON
  • 先贴一份JSON数据
    {
        "afterColumns":{
            "created":"1589186680",
            "extra":{
                "canGiving":false
            },
            "parameter":[
                1,
                2,
                3,
                4
            ]
        },
        "beforeColumns":null,
        "tableVersion":{
            "binlogFile":null,
            "binlogPosition":0,
            "version":0
        },
        "touchTime":1589186680591
    }
    
  • 这是我们的binlog解析工具生成的JSON,有ARRAY、OBJECT、STRING、NUMBER、BOOLEAN,基本上常用的都有了
  • 再贴一份JSON数据类型和FLINK SQL数据类型的映射关系
    SON模式 Flink SQL
    bject ROW
    oolean BOOLEAN
    rray ARRAY[_]
    umber DECIMAL
    nteger DECIMAL
    tring STRING
    tring 与 format: date-time TIMESTAMP
    tring 与 format: date DATE
    tring 与 format: time TIME
    tring 与 encoding: base64 ARRAY[TINYINT]
    ull NULL (尚不支持)
  • 伪代码给大家演示一下
	首先我们的JSON最外层是个OBJECT对象,有4个字段分别是afterColumns,beforeColumns,tableVersion,touchTime。其中afterColumns又是个嵌套JSON,也就是OBJECT对象;beforeColumns是个STRING类型;tableVersion同样也是个嵌套JSON;touchTime是number类型。所以我们最开始的DDL是这样的
	CREATE TABLE t1 ( 
	afterColumns ROW(),
	beforeColumns STRING,
	tableVersion ROW(),
	touchTime BIGINT
	)WITH (
    'connector.type' = 'kafka',  -- 指定连接类型是kafka
    'connector.version' = '0.11',  -- 与我们之前Docker安装的kafka版本要一致
    'connector.topic' = 'json_parse', -- 之前创建的topic 
    'connector.properties.group.id' = 'flink-test-0', -- 消费者组,相关概念可自行百度
    'connector.startup-mode' = 'earliest-offset',  --指定从最早消费
    'connector.properties.zookeeper.connect' = 'localhost:2181',  -- zk地址
    'connector.properties.bootstrap.servers' = 'localhost:9092',  -- broker地址
    'format.type' = 'json'  -- json格式,和topic中的消息格式保持一致
	) 		
		
 用FLINK SQL执行 select * from t1 看看,得到的结果是 ,null,,1589186680591
 afterColumns 和 tableVersion 都是空,另外两个字段都打印出来了,原因是afterColumns 和 tableVersion都是OBJECT类型,FLINK SQL还是没法帮我们智能识别出来,所以只能自己定义的更详细点。
 afterColumns中有三个字段,分别是created,extra,parameter,对应着STRING,OBJECT,ARRAY类型;extra中有一个字段canGiving,类型是BOOLEAN。
 tableVersion中也有三个字段,分别是binlogFile,binlogPosition,version,都是简单类型,分别是STRING,INTEGER,INTEGER。
 所以,我们新的DDL语句时这样的
	 CREATE TABLE t1 ( 
	afterColumns ROW(created STRING,extra ROW(canGiving BOOLEAN),`parameter` ARRAY ) ,
	beforeColumns STRING ,
	tableVersion ROW(binlogFile STRING,binlogPosition INT ,version INT) ,
	touchTime bigint 
	) WITH (
	    'connector.type' = 'kafka',  -- 指定连接类型是kafka
	    'connector.version' = '0.11',  -- 与我们之前Docker安装的kafka版本要一致
	    'connector.topic' = 'json_parse', -- 之前创建的topic 
	    'connector.properties.group.id' = 'flink-test-0', -- 消费者组,相关概念可自行百度
	    'connector.startup-mode' = 'earliest-offset',  --指定从最早消费
	    'connector.properties.zookeeper.connect' = 'localhost:2181',  -- zk地址
	    'connector.properties.bootstrap.servers' = 'localhost:9092',  -- broker地址
	    'format.type' = 'json'  -- json格式,和topic中的消息格式保持一致
	)
	这里我们注意一下,parameter得用``括起来,因为parameter是个关键字,想用的话得用``包围,所有关键字可以参考https://ci.apache.org/projects/flink/flink-docs-release-1.10/dev/table/sql/#reserved-keywords
	最后输出的数据是这样的:1589186680,false,[1, 2, 3, 4],null,null,0,0,1589186680591,和我们JSON中的数据一样,没毛病!
	不过我们用的是select *,正常来说我们应该只输出想要的字段。那么,假设我们只想拿到canGiving 这个嵌套好几层的字段值,该怎么写呢?
	其实很简单,就像是包路径一样 afterColumns.extra.canGiving ,试一下
	select  afterColumns.extra.canGiving from t1
	输出:false
	再试一下输出 parameter 这个集合的第一个值
	select afterColumns.`parameter`[0] from t1
	报错了:Caused by: java.lang.ArrayIndexOutOfBoundsException: -1
  很纳闷,长度为4的数组,取数组序号为0,也就是数组中第一个数,竟然报角标越界了,换成afterColumns.`parameter`[1]试试看呢?
  震惊!竟然输出了1,也就是我们数组中的第一个字段。可在我们JAVA应用或者HIVE SQL中,数组不是应该是从0开始吗?
  因为 Flink 中数组下标是从 1 开始的,在 Validation 阶段发现下标是小于等于 0 的时候抛出异常
  • 目前只需要在指定FLINK SQL的字段类型就可以了,format.json-schema目前可以不填,能够自动根据字段类型映射上

维表关联HBASE

  • 之前和大家聊过维表的作用和如何使用MYSQL维表,今天来说说HBASE维表
  • 首先还是老样子,Docker装一下HBASEdocker run -d -h myhbase -p 2182:2181 -p 8080:8080 -p 8085:8085 -p 9090:9090 -p 9095:9095 -p 16000:16000 -p 16010:16010 -p 16201:16201 -p 16020:16020 -p 16301:16301 --name hbase1.4.3 harisekhon/hbase:1.4这里-p 后面跟的是要映射的端口。冒号前是本地端口,冒号后是Docker容器的端口,这里记得把容器的2181映射到本地的2182端口上,因为本地的2181已经被之前安装的ZK给占用了,如果还用2181的话报错;而且因为HBASE也依赖于ZK,容器里这个ZK我们连接的时候也需要使用,所以把端口映射到2182上。另外大家记好这个容器的名字myhbase,后面要用上
  • 装完之后进入容器docker exec -it '容器ID,不要照着复制!' /bin/bash。进去之后执行hbase shell 进入HBASE的命令行,建表create 't2',{NAME => 'f1'},PUT一条数据put 't2','1589186680','f1:col1','2020-05-11 16:44:40',再查一下我们建的表,看数据是否存在里面scan 't2',能看到我们之前PUT进去的数据
  • 再去打开本地的HOST文件,加入127.0.01 myhbase,如果少了这一步操作,而且你的FLINK任务没开INFO级别的日志,那么你会发现你的任务一直没有输出,也不报错。开了INFO之后,能看到client.RpcRetryingCaller - Call exception, tries=10, retries=35, started=47363 ms ago, cancelled=false, msg=can not resolve myhbase,16020,1590717943339 row 't2,1589186680,99999999999999' on table 'hbase:meta' at region=hbase:meta,,1.1588230740, hostname=myhbase,16020,1590717943339, seqNum=0大概意思就是在hbase:meta也就是HBASE的元数据里面,找不到myhbase
  • 贴一下完整代码
    package FlinkSql;
    
    import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
    import org.apache.flink.table.api.EnvironmentSettings;
    import org.apache.flink.table.api.Table;
    import org.apache.flink.table.api.java.StreamTableEnvironment;
    import org.apache.flink.types.Row;
    
    
    public class FlinkSql06 {
    
        public static final String KAFKA_TABLE_SOURCE_DDL_01 = "" +
                "CREATE TABLE t1 ( \n" +
                "afterColumns ROW(created STRING,extra ROW(canGiving BOOLEAN),`parameter` ARRAY ) ,\n" +
                "beforeColumns STRING ,\n" +
                "tableVersion ROW(binlogFile STRING,binlogPosition INT ,version INT) ,\n" +
                "p AS PROCTIME(),\n"+
                "touchTime bigint \n" +
                ") WITH (\n" +
                "    'connector.type' = 'kafka',  -- 指定连接类型是kafka\n" +
                "    'connector.version' = '0.11',  -- 与我们之前Docker安装的kafka版本要一致\n" +
                "    'connector.topic' = 'json_parse', -- 之前创建的topic \n" +
                "    'connector.properties.group.id' = 'flink-test-0', -- 消费者组,相关概念可自行百度\n" +
                "    'connector.startup-mode' = 'earliest-offset',  --指定从最早消费\n" +
                "    'connector.properties.zookeeper.connect' = 'localhost:2181',  -- zk地址\n" +
                "    'connector.properties.bootstrap.servers' = 'localhost:9092',  -- broker地址\n" +
                "    'format.type' = 'json'  -- json格式,和topic中的消息格式保持一致\n" +
                ")";
    
        public static final String HBASE_TABLE_DIM_DDL = "" +
                "CREATE TABLE t2 (\n" +
                "rowkey String,\n" +
                "f1 ROW\n" +
                ") WITH (\n" +
                "  'connector.type' = 'hbase', -- required: specify this table type is hbase\n" +
                "  'connector.version' = '1.4.3',          -- required: valid connector versions are \"1.4.3\"\n" +
                "  'connector.table-name' = 't2',  -- required: hbase table name\n" +
                "  'connector.zookeeper.quorum' = 'localhost:2182', -- required: HBase Zookeeper quorum configuration\n" +
                "  'connector.zookeeper.znode.parent' = '/hbase'    -- optional: the root dir in Zookeeper for HBase cluster.\n" +
                ")";
    
        public static void main(String[] args) throws Exception {
    
            //构建StreamExecutionEnvironment
            StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
    
            //构建EnvironmentSettings 并指定Blink Planner
            EnvironmentSettings bsSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();
    
            //构建StreamTableEnvironment
            StreamTableEnvironment tEnv = StreamTableEnvironment.create(env, bsSettings);
    
            //执行DDL
            tEnv.sqlUpdate(KAFKA_TABLE_SOURCE_DDL_01);
    
            tEnv.sqlUpdate(HBASE_TABLE_DIM_DDL);
    
            //注意FOR SYSTEM_TIME AS OF a.p AS b 的用法,左表需要用proctime,我在DDL指定了,大家记得看好
            Table table = tEnv.sqlQuery("select a.* ,b.* from t1 a left join  t2 FOR SYSTEM_TIME AS OF a.p AS b on a.afterColumns.created = b.rowkey");
    
            //转成流输出
            tEnv.toAppendStream(table, Row.class).print().setParallelism(1);
    
            env.execute("Flink Sql 06");
        }
    }
    
    
  • windows跑的时候可能会出现Could not locate executable null\bin\winutils.exe in the Hadoop binaries,首先得去下载HADOOP客户端,这里有所有的HADOOP版本,我用的是2.7的版本,然后配置一下HADOOP_HOME环境变量。配置完可能得重启,不然IDEA里面的环境变量不生效。也可以在类的启动配置里面指定一下环境变量HADOOP_HOME=D:\hadoop-2.7.0\hadoop-2.7.0。然后去这里下载exe文件,并且丢到HADOOP_HOME的bin路径下,比如我的就是D:\hadoop-2.7.0\hadoop-2.7.0\bin里面。再执行任务,就能看到正常输出了

提醒

  • 大家pom.xml记得加一下这2个依赖
		
        
            org.apache.flink
            flink-hbase_2.11
            1.10.0
        
        
            org.apache.hadoop
            hadoop-common
            2.7.0
        
  • 一个是FLINK-HBASE的连接器
  • 一个是HADOOP的依赖

大家记得给我点赞关注收藏呀!!!

你可能感兴趣的:(Flink,Sql)