6.Flink實時專案之業務資料分流

選手一號位發表於2022-02-22

在上一篇文章中,我們已經獲取到了業務資料的輸出流,分別是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 --->>");

處理流程如下:

6.Flink實時專案之業務資料分流

自定義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;

    6.Flink實時專案之業務資料分流

資料已經實時同步到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 外部

相關文章