数据从kafka到hive(2)

本文详细介绍了数据从Kafka到Hive的迁移工具camus的实现原理,包括camus如何重写InputFormat和OutputFormat,以及在ETL过程中对kafka partition的处理方式。camus通过map-reduce执行,不设置reduce任务,直接将数据写入目标目录。在InputFormat中,camus获取每个partition的earliest和latest offset,创建请求列表并分配到mapper。RecordReader从InputSplit读取message,并传递给mapper。OutputFormat则负责将mapper的输出写入临时文件,任务完成后,将文件拷贝到最终目的地。

前面一篇讲到了将数据从kafka读到hdfs使用了开源工具camus,既然用到了开源的代码,免不了研究一下实现过程。所以这里分享一下阅读camus代码了解到的一些细节。

前置知识

在讲camus之前,需要提一下hadoop的一些知识。

关于inputFormat

inputFormat类的原型如下:

public interface InputFormat<K, V> {
        InputSplit[] getSplits(JobConf job, int numSplits) throws IOException;
        RecordReader<K, V> getRecordReader(InputSplit split,JobConf job,Reporter reporter) throws IOException;
}

hadoop job调用setInputFormat来设置InputFormat类,通过getSplits函数,hadoop可以将输入分割为InputSplit,每个map分配到InputSplit后,再通过getRecordReader获取reader,逐条读取输入文件中Key-Value。hadoop本身提供了一些InputFormat类的实现,如TextInputFormat,KeyValueTextInputFormat,SequenceFileInputFormat等;
除了使用hadoop本身提供的这些类以外,hadoop允许自定义InputFormat类,只需要实现相应的getSplits函数和RecoderReader类即可。

关于OutputFormat 和 OutputCommitter

OutputFormat类用于写入记录,原型如下:

public abstract class OutputFormat<K, V> {
    //创建一个写入器
  public abstract RecordWriter<K, V> getRecordWriter(TaskAttemptContext context) throws IOException, InterruptedException;
   // 检查结果输出的存储空间是否有效
  public abstract void checkOutputSpecs(JobContext context) throws IOException, InterruptedException;
   //创建一个任务提交器
  public abstract OutputCommitter getOutputCommitter(TaskAttemptContext context) throws IOException, InterruptedException;
}

getRecordWriter和checkOutputSpecs都很好理解,前置就是将Mapper或Reducer传来的key-value写入到文件里面,写入的姿势由它来决定。而checkOutputSpecs一般就是用来检查输出规范,判断输出文件是否存在,如果已经存在,则抛出异常。
OutputCommitter稍稍难理解一点,里面定义了这几个方法:

//Job开始被执行之前,框架会调用setupJob()为Job创建一个输出路径
void  setupJob(JobContext jobContext);
//如果Job成功完成,框架会调用commitJob()提交Job的输出
void  commitJob(JobContext jobContext);
//如果Job失败,框架会调用OutputCommitter.abortJob()撤销Job的输出
void  abortJob(JobContext jobContext, JobStatus.State state);
//Task执行前的准备工作,类似setupJob
void  setupTask(TaskAttemptContext taskContext);
//Task可能没有输出,也就不需要提交,通过needsTaskCommit()来判断
boolean  needsTaskCommit(TaskAttemptContext taskContext);
//Task成功执行,提交输出
void  commitTask(TaskAttemptCont
使用Flink 1.17.1的Java API实现从KafkaHive数据传输,可按以下步骤进行: ### 1. 环境准备 需确保Kafka集群、Hive Metastore正常运行,同时准备好Flink 1.17.1环境。对于Flink集成Hive Metastore,可参考EMR官方文档给出的脚本(以EMR 6.15 (Flink 1.17.1)版本为例): ```bash sudo -u flink cp /usr/lib/hive/lib/antlr-runtime-3.5.2.jar /usr/lib/flink/lib sudo -u flink cp /usr/lib/hive/lib/hive-exec-3.1.3*.jar /usr/lib/flink/lib sudo -u flink cp /usr/lib/hive/lib/libfb303-0.9.3.jar /usr/lib/flink/lib sudo -u flink cp /usr/lib/flink/opt/flink-connector-hive_2.12-1.17.1-amzn-1.jar /usr/lib/flink/lib ``` 此脚本将Hive相关的JAR包复制到Flink的lib目录下,以保证Flink能与Hive进行交互[^5]。 ### 2. 添加Maven依赖 在项目的`pom.xml`文件中添加Flink、KafkaHive相关的依赖: ```xml <dependencies> <!-- Flink核心依赖 --> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-java</artifactId> <version>1.17.1</version> </dependency> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-streaming-java_2.12</artifactId> <version>1.17.1</version> </dependency> <!-- Flink Kafka连接器 --> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-connector-kafka_2.12</artifactId> <version>1.17.1</version> </dependency> <!-- Flink Hive连接器 --> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-connector-hive_2.12</artifactId> <version>1.17.1</version> </dependency> <!-- Hive Metastore依赖 --> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-metastore</artifactId> <version>3.1.3</version> </dependency> </dependencies> ``` ### 3. Java代码实现 以下是一个使用Flink 1.17.1的Java API从Kafka读取数据并写入Hive的示例代码: ```java import org.apache.flink.api.common.serialization.SimpleStringSchema; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer; import org.apache.flink.table.api.Table; import org.apache.flink.table.api.bridge.java.StreamTableEnvironment; import org.apache.flink.table.catalog.hive.HiveCatalog; import java.util.Properties; public class KafkaToHive { public static void main(String[] args) throws Exception { // 创建流执行环境 StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env); // 配置Kafka连接属性 Properties kafkaProps = new Properties(); kafkaProps.setProperty("bootstrap.servers", "localhost:9092"); kafkaProps.setProperty("group.id", "flink-kafka-consumer"); // 创建Kafka数据源 FlinkKafkaConsumer<String> kafkaConsumer = new FlinkKafkaConsumer<>("your-kafka-topic", new SimpleStringSchema(), kafkaProps); DataStream<String> kafkaStream = env.addSource(kafkaConsumer); // 注册Hive Catalog String catalogName = "myhive"; String defaultDatabase = "default"; String hiveConfDir = "/path/to/hive/conf"; HiveCatalog hiveCatalog = new HiveCatalog(catalogName, defaultDatabase, hiveConfDir); tableEnv.registerCatalog(catalogName, hiveCatalog); tableEnv.useCatalog(catalogName); // 将Kafka数据流转换为表 Table kafkaTable = tableEnv.fromDataStream(kafkaStream); // 定义Hive表的DDL语句 String createHiveTableDDL = "CREATE TABLE IF NOT EXISTS your_hive_table (" + "message STRING" + ") STORED AS PARQUET"; tableEnv.executeSql(createHiveTableDDL); // 将Kafka表的数据插入到Hive表中 String insertSql = "INSERT INTO your_hive_table SELECT * FROM " + kafkaTable; tableEnv.executeSql(insertSql); // 执行Flink作业 env.execute("Kafka to Hive"); } } ``` ### 4. 代码解释 - **环境创建**:创建Flink的流执行环境和表执行环境。 - **Kafka连接**:配置Kafka连接属性,创建Kafka数据源。 - **Hive集成**:注册Hive Catalog,使用Hive Catalog。 - **数据转换**:将Kafka数据流转换为表。 - **Hive表创建**:使用DDL语句在Hive中创建表。 - **数据插入**:将Kafka表的数据插入到Hive表中。 - **作业执行**:执行Flink作业。 ### 5. 注意事项 - 要根据实际情况修改Kafka的`bootstrap.servers`、`group.id`和`topic`。 - 要根据实际情况修改Hive的配置文件路径`hiveConfDir`。 - 要根据实际情况修改Hive表的表名和字段名。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值