在上一篇文章中,我们已经获取到了业务数据的输出流,分别是dim层维度数据的输出流,及dwd层事实数据的输出流,接下来我们要做的就是把这些输出流分别再流向对应的数据介质中,dim层流向hbase中,dwd层依旧回写到kafka中。

1.分流维度表sink到hbase

上一篇的结果是维度数据在侧输出流hbaseDs,事实数据在主流filterDs中,如下:

//5.动态分流,事实表写会kafka,维度表写入hbase
OutputTag<JSONObject> hbaseTag = new OutputTag<JSONObject>(TableProcess.SINK_TYPE_HBASE){};
//创建自定义mapFunction函数
SingleOutputStreamOperator<JSONObject> kafkaTag = filterDs.process(new TableProcessFunction(hbaseTag));
DataStream<JSONObject> hbaseDs = kafkaTag.getSideOutput(hbaseTag);
filterDs.print("json str --->>");

处理流程如下:

自定义RickSinkFunction类:DimSink.java

  • 初始化phoenix连接

  • 保存数据

1.1 配置

在BaseDbTask任务中,我们已经获取到hbase的输出流,然后就可以开始hbase的一系列操作了。

添加phoenix依赖包

<!-- phoenix -->
<dependency>
   <groupId>org.apache.phoenix</groupId>
   <artifactId>phoenix-spark</artifactId>
   <version>5.0.0-HBase-2.0</version>
   <exclusions>
       <exclusion>
           <groupId>org.glassfish</groupId>
           <artifactId>javax.el</artifactId>
       </exclusion>
   </exclusions>
</dependency>

修改hbase-site.xml,因为要用单独的 schema,所以在 Idea 程序中也要加入 hbase-site.xml

为了开启 hbase 的 namespace 和 phoenix 的 schema 的映射,在程序中需要加这个配置文件,另外在 linux 服务上,也需要在 hbase 以及 phoenix 的 hbase-site.xml 配置文件中,加上以上两个配置,并使用 xsync 进行同步。

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
   <property>
       <name>hbase.rootdir</name>
       <value>hdfs://hadoop101:9000/hbase</value>
   </property>
   <property>
       <name>hbase.cluster.distributed</name>
       <value>true</value>
   </property>
   <property>
       <name>hbase.zookeeper.quorum</name>
       <value>hadoop101,hadoop102,hadoop103</value>
   </property>
   <property>
       <name>hbase.table.sanity.checks</name>
       <value>false</value>
   </property>
   <property>
       <name>phoenix.schema.isNamespaceMappingEnabled</name>
       <value>true</value>
   </property>
   <property>
       <name>phoenix.schema.mapSystemTablesToNamespace</name>
       <value>true</value>
   </property>
</configuration>

1.2 创建命名空间

在phoenix中执行

create schema GMALL_REALTIME;

1.3 DimSink.java

自定义addSink类

package com.zhangbao.gmall.realtime.app.func;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.zhangbao.gmall.realtime.common.GmallConfig;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @author: zhangbao
* @date: 2021/9/4 12:23
* @desc: 将维度表写入hbase中
**/
@Log4j2
public class DimSink extends RichSinkFunction<JSONObject> {
   private Connection conn = null;

   @Override
   public void open(Configuration parameters) throws Exception {
       log.info("建立 phoenix 连接...");
       Class.forName("org.apache.phoenix.jdbc.PhoenixDriver");
       conn = DriverManager.getConnection(GmallConfig.PHOENIX_SERVER);
       log.info("phoenix 连接成功!");
  }

   @Override
   public void invoke(JSONObject jsonObject, Context context) throws Exception {
       String sinkTable = jsonObject.getString("sink_table");
       JSONObject data = jsonObject.getJSONObject("data");
       PreparedStatement ps = null;
       if(data!=null && data.size()>0){
           try {
               //生成phoenix的upsert语句,这个包含insert和update操作
               String sql = generateUpsert(data,sinkTable.toUpperCase());
               log.info("开始执行 phoenix sql -->{}",sql);
               ps = conn.prepareStatement(sql);
               ps.executeUpdate();
               conn.commit();
               log.info("执行 phoenix sql 成功");
          } catch (SQLException throwables) {
               throwables.printStackTrace();
               throw new RuntimeException("执行 phoenix sql 失败!");
          }finally {
               if(ps!=null){
                   ps.close();
              }
          }
      }
  }

   //生成 upsert sql
   private String generateUpsert(JSONObject data, String sinkTable) {
       StringBuilder sql = new StringBuilder();
       //upsert into scheme.table(id,name) values('11','22')
       sql.append("upsert into "+GmallConfig.HBASE_SCHEMA+"."+sinkTable+"(");
       //拼接列名
       sql.append(StringUtils.join(data.keySet(),",")).append(")");
       //填充值
       sql.append("values('"+ StringUtils.join(data.values(),"','")+"')");
       return sql.toString();
  }
}

然后在主程序中加入

//6. 将维度表写入hbase中
hbaseDs.addSink(new DimSink());

1.4 测试

  • 需要启动的服务

    hdfs、zk、kafka、Maxwell、hbase,BaseDbTask.java

  • 修改配置数据:gmall2021_realtime.table_process

    INSERT INTO `gmall2021_realtime`.`table_process` (`source_table`, `operate_type`, `sink_type`, `sink_table`, `sink_columns`, `sink_pk`, `sink_extend`) VALUES ('base_trademark', 'insert', 'hbase', 'dim_base_trademark', 'id,tm_name', 'id', NULL);

    此条配置数据代表,如果表base_trademark有插入数据,就把数据同步到hbase中,自动建表,作为维度数据。

  • 修改业务库中表数据:gmall2021.base_trademark

    INSERT INTO `gmall2021`.`base_trademark` (`id`, `tm_name`, `logo_url`) VALUES ('15', '55', '55');
  • 查看phoenix数据:select * from GMALL_REALTIME.BASE_TRADEMARK;

数据已经实时同步到hbase中。

2.分流事实表sink到kafka

2.1 MyKafkaUtil定义新方法

在MyKafkaUtil中定义新的生产者方法,可动态指定topic,如果不指定则生产到默认topic:default_data

/**
    * 动态生产到不同的topic,如果不传topic,则自动生产到默认的topic
    * @param T 序列化后的数据,可指定topic
    */
   public static <T> FlinkKafkaProducer<T> getKafkaBySchema(KafkaSerializationSchema<T> T){
       Properties pros = new Properties();
       pros.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,KAFKA_HOST);
       return new FlinkKafkaProducer<T>(DEFAULT_TOPIC,T,pros,FlinkKafkaProducer.Semantic.EXACTLY_ONCE);
  }

在主任务BaseDbTask中使用

//7. 将事实数据写回到kafka
FlinkKafkaProducer<JSONObject> kafkaBySchema = MyKafkaUtil.getKafkaBySchema(new KafkaSerializationSchema<JSONObject>() {
   @Override
   public void open(SerializationSchema.InitializationContext context) throws Exception {
       System.out.println("kafka serialize open");
  }
   @Override
   public ProducerRecord<byte[], byte[]> serialize(JSONObject jsonObject, @Nullable Long aLong) {
       String sinkTopic = jsonObject.getString("sink_table");
       return new ProducerRecord<>(sinkTopic, jsonObject.getJSONObject("data").toString().getBytes());
  }
});
kafkaTag.addSink(kafkaBySchema);

2.2 测试

  • 需要启动的服务

    hdfs、zk、kafka、Maxwell、hbase,BaseDbTask.java

  • 修改配置信息:gmall2021_realtime.table_process

    INSERT INTO `gmall2021_realtime`.`table_process` (`source_table`, `operate_type`, `sink_type`, `sink_table`, `sink_columns`, `sink_pk`, `sink_extend`) VALUES ('order_info', 'insert', 'kafka', 'dwd_order_info', 'id,consignee,consignee_tel,total_amount,order_status,user_id,payment_way,delivery_address,order_comment,out_trade_no,trade_body,create_time,operate_time,expire_time,process_status,tracking_no,parent_order_id,img_url,province_id,activity_reduce_amount,coupon_reduce_amount,original_total_amount,feight_fee,feight_fee_reduce,refundable_time', 'id', NULL);

    表示表order_info有插入数据,就会同步到kafka中,topic为dwd_order_info。

  • 启动kafka消费者,查看是否有数据进来

    [zhangbao@hadoop101 root]$ cd /opt/module/kafka/bin/

    [zhangbao@hadoop101 bin]$ ./kafka-console-consumer.sh --bootstrap-server hadoop101:9092,hadoop102:9092,hadoop103:9092 --topic dwd_order_info

  • 最后启动业务数据生成服务:mock-db-0.0.1-SNAPSHOT.jar

    记得先修改配置文件的生成日期:2021-09-12

最后查看kafka消费者可以看到有数据产生,说明流程已经走通。

3.算子选择简介

function 可转换结构 可过滤数据 侧输出 open 可以使用状态 输出至
MapFunction Yes         下游算子
FilterFunction   Yes       下游算子
RichMapFunction Yes     Yes Yes 下游算子
RichFilterFunction   Yes   Yes Yes 下游算子
ProcessFunction Yes Yes Yes Yes Yes 下游算子
SinkFunction Yes Yes       外部
RichSinkFunction Yes Yes   Yes Yes 外部

6.Flink实时项目之业务数据分流的更多相关文章

  1. 5.Flink实时项目之业务数据准备

    1. 流程介绍 在上一篇文章中,我们已经把客户端的页面日志,启动日志,曝光日志分别发送到kafka对应的主题中.在本文中,我们将把业务数据也发送到对应的kafka主题中. 通过maxwell采集业务数 ...

  2. 3.Flink实时项目之流程分析及环境搭建

    1. 流程分析 前面已经将日志数据(ods_base_log)及业务数据(ods_base_db_m)发送到kafka,作为ods层,接下来要做的就是通过flink消费kafka 的ods数据,进行简 ...

  3. 7.Flink实时项目之独立访客开发

    1.架构说明 在上6节当中,我们已经完成了从ods层到dwd层的转换,包括日志数据和业务数据,下面我们开始做dwm层的任务. DWM 层主要服务 DWS,因为部分需求直接从 DWD 层到DWS 层中间 ...

  4. 10.Flink实时项目之订单维度表关联

    1. 维度查询 在上一篇中,我们已经把订单和订单明细表join完,本文将关联订单的其他维度数据,维度关联实际上就是在流中查询存储在 hbase 中的数据表.但是即使通过主键的方式查询,hbase 速度 ...

  5. 4.Flink实时项目之数据拆分

    1. 摘要 我们前面采集的日志数据已经保存到 Kafka 中,作为日志数据的 ODS 层,从 kafka 的ODS 层读取的日志数据分为 3 类, 页面日志.启动日志和曝光日志.这三类数据虽然都是用户 ...

  6. 9.Flink实时项目之订单宽表

    1.需求分析 订单是统计分析的重要的对象,围绕订单有很多的维度统计需求,比如用户.地区.商品.品类.品牌等等.为了之后统计计算更加方便,减少大表之间的关联,所以在实时计算过程中将围绕订单的相关数据整合 ...

  7. 1.Flink实时项目前期准备

    1.日志生成项目 日志生成机器:hadoop101 jar包:mock-log-0.0.1-SNAPSHOT.jar gmall_mock ​ |----mock_common ​ |----mock ...

  8. 11.Flink实时项目之支付宽表

    支付宽表 支付宽表的目的,最主要的原因是支付表没有到订单明细,支付金额没有细分到商品上, 没有办法统计商品级的支付状况. 所以本次宽表的核心就是要把支付表的信息与订单明细关联上. 解决方案有两个 一个 ...

  9. 8.Flink实时项目之CEP计算访客跳出

    1.访客跳出明细介绍 首先要识别哪些是跳出行为,要把这些跳出的访客最后一个访问的页面识别出来.那么就要抓住几个特征: 该页面是用户近期访问的第一个页面,这个可以通过该页面是否有上一个页面(last_p ...

随机推荐

  1. 使用NSSM把任意exe程序注册成Windows服务

    1.登录NSSM官网下载最新版本: http://www.nssm.cc/download 2.解压之后可以看到win32和win64位的nssm.exe,根据自己的电脑选择使用 3.拷贝一份nssm ...

  2. Oracle update和select 关联

    Oracle update和select 关联 目录 Oracle update和select 关联 1.介绍 2.解决方法 2.1.需求 2.2.错误演示 2.3.解决方法 1.介绍 本文主要向大家 ...

  3. 【C++】字符串处理

    字符串处理 标签:c++ 目录 字符串处理 一.输入 1. scanf()函数 2. gets()函数 3. getchar()函数 二.输出 1. printf()函数 2. puts()函数: 3 ...

  4. Cesium入门5 - Cesium ion

    Cesium入门5 - Cesium ion Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ Cesium io ...

  5. 论文解读GALA《Symmetric Graph Convolutional Autoencoder for Unsupervised Graph Representation Learning》

    论文信息 Title:<Symmetric Graph Convolutional Autoencoder for Unsupervised Graph Representation Learn ...

  6. vector自实现(一)

    vector.h: #ifndef __Vector__H__ #define __Vector__H__ typedef int Rank; #define DEFAULT_CAPACITY 3 t ...

  7. shell $用法

    $0 这个程式的执行名字$n 这个程式的第n个参数值,n=1..9$* 这个程式的所有参数,此选项参数可超过9个.$# 这个程式的参数个数$$ 这个程式的PID(脚本运行的当前进程ID号)$! 执行上 ...

  8. 抽象类 final

    抽象类 1.用abstract关键字来修饰一个类时,这个类叫做抽象类,用abstract来修饰一个方法时,这个方法叫抽象方法. 2.含有抽象方法的类必须被声明为抽象类,抽象类必须被继承,抽象方法必须被 ...

  9. Discriminative Learning of Deep Convolutional Feature Point Descriptors 论文阅读笔记

    介绍 该文提出一种基于深度学习的特征描述方法,并且对尺度变化.图像旋转.透射变换.非刚性变形.光照变化等具有很好的鲁棒性.该算法的整体思想并不复杂,使用孪生网络从图块中提取特征信息(得到一个128维的 ...

  10. TensorFlow 深度学习中文第二版·翻译完成

    原文:Deep Learning with TensorFlow Second Edition 协议:CC BY-NC-SA 4.0 不要担心自己的形象,只关心如何实现目标.--<原则>, ...