DataStreamAPI之Sink

1.KafkaSink

使用kafkaSink首先需要添加依赖

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-kafka_2.12</artifactId>
    <version>1.13.1</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>

最重要的就是创建一个Flink程序用的生产者

FlinkKafkaProducer<String> kafkaProducer = new FlinkKafkaProducer<>("hadoop102:9092", "first", new SimpleStringSchema());

其中String泛型代表你写入到kafka的数据类型

package net.cyan.Sink;
import com.alibaba.fastjson.JSON;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.streaming.api.SimpleTimerService;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer;

public class Demo1_kafkaSink {
   
   
    public static void main(String[] args) {
   
   
        //创建执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        //创建FlinkKafkaProducer
        FlinkKafkaProducer<String> kafkaProducer = new FlinkKafkaProducer<>("hadoop102:9092", "first", new SimpleStringSchema());

        env.socketTextStream("hadoop103",9999)
               //map处理数据
                .map(new MapFunction<String, String>() {
   
   
                    @Override
                    public String map(String s) throws Exception {
   
   
                        //切割数据集
                        String[] split = s.split(",");
                        //转换
                        return JSON.toJSONString(split[0].concat(split[1]).concat(split[2]));
                    }
                })
                 //添加
                .addSink(kafkaProducer);

        try {
   
   
            //启动执行环境
            env.execute();
        } catch (Exception e) {
   
   
            e.printStackTrace();
        }

    }
}

当你写入到kafka的数据类型为String时,基本没问题。当你使用自定义POJO(等同于Bean)来封装你写入的数据时,那么上述producer方法中的默认序列化器就不能序列化了,那么我们就需要使用它的重载方法

2.自定义MySqlSink

自定义一个Sink,向mysql写入数据

第一步,导入依赖

<think>我们正在讨论FlinkDataStreamAPI中的转换操作,特别是map、filter方法,以及设置name、uid和parallelism的作用。结合Kafka数据源和StarRocksSink的使用场景,我们可以这样解释:###1.**DataStream转换操作的核心概念**在Flink中,DataStreamAPI的核心操作分为三类:**Source**(数据源)、**Transformation**(转换操作)和**Sink**(数据输出)。其中转换操作是数据处理的核心,包括`map`、`filter`、`flatMap`等。####(1)`map`方法-**作用**:对数据流中的每个元素应用一个函数,生成一个新的元素。输入一个元素,输出一个元素。-**示例**:将Kafka读取的字符串转换为大写:```javaDataStream<String>upperStream=kafkaSourceStream.map(str->str.toUpperCase());```-**使用场景**:在从Kafka读取数据后,可能需要将原始数据(如JSON字符串)转换为Java对象,或者进行字段提取。####(2)`filter`方法-**作用**:对数据流中的每个元素进行条件判断,只保留满足条件的元素。-**示例**:过滤掉长度小于5的字符串:```javaDataStream<String>filteredStream=kafkaSourceStream.filter(str->str.length()>=5);```-**使用场景**:在写入StarRocks前,可能需要过滤掉无效数据(如空值、异常值)。###2.**算子标识:`name`和`uid`**在Flink作业中,每个算子都可以设置`name`(名称)和`uid`(唯一标识符)。这两个配置对于作业的容错和运维至关重要。-**`.name("sink_starrocks")`**:-作用:为算子设置一个可读的名称,用于在FlinkWebUI或日志中标识该算子。-重要性:便于监控和调试。例如,在WebUI中可以看到名为`sink_starrocks`的Sink算子的吞吐量和延迟。-**`.uid("sink_starrocks")`**:-作用:为算子设置全局唯一的标识符(User-DefinedIdentifier)。-**核心价值**:-**故障恢复**:Flink的检查点(Checkpoint)机制依赖`uid`来关联算子的状态。如果作业重启,Flink通过`uid`识别并恢复状态[^3]。-**资源管理**:在调整并行度或修改作业时,`uid`帮助Flink精确控制算子的状态生命周期。-**最佳实践**:**必须为每个有状态的算子设置`uid`**,否则作业升级时可能导致状态无法恢复!###3.**并行度设置:`.setParallelism(outp)`**-**作用**:指定算子的并行度(即运行该算子的任务实例数量)。-**示例**:设置StarRocksSink的并行度为`outp`(一个变量值):```javamap_filter_return.addSink(starRocksSink).name("sink_starrocks").uid("sink_starrocks").setParallelism(outp);//并行度由变量outp控制```-**使用场景**:-**性能调优**:根据数据量和硬件资源调整并行度。例如,如果Kafka主题有10个分区,通常设置Source并行度为10;而Sink的并行度可根据StarRocks的写入能力调整。-**资源隔离**:避免某个算子消耗过多资源。例如,如果StarRocks集群写入能力有限,可降低Sink并行度。###4.**结合Kafka和StarRocks的完整示例假设一个场景:从Kafka读取日志数据,过滤无效记录,提取关键字段后写入StarRocks。```java//1.创建Kafka数据源(引用[2])Propertiesprops=newProperties();props.setProperty("bootstrap.servers","kafka:9092");DataStream<String>kafkaSource=env.addSource(newFlinkKafkaConsumer<>("topic_log",newSimpleStringSchema(),props)).name("kafka_source").uid("kafka_source");//2.转换操作:过滤和映射DataStream<LogRecord>processedStream=kafkaSource.filter(log->isValid(log))//过滤无效日志.map(log->parseLog(log))//解析为LogRecord对象.name("map_filter")//设置转换操作名称.uid("map_filter");//设置转换操作UID(无状态时可省略)//3.构建StarRocksSink(引用用户提供的代码)SinkFunctionstarRocksSink=StarRocksBuilder.buildSinkForCluster(parameterTool);processedStream.addSink(starRocksSink).name("sink_starrocks").uid("sink_starrocks").setParallelism(4);//设置Sink并行度//4.触发作业执行(引用[3])env.execute("KafkaToStarRocksJob");```###5.关键配置的意义总结|配置项|作用场景|最佳实践||----------------|-----------------------------------|-----------------------------------||**`map`**|数据格式转换(如JSON→对象)|避免复杂计算,保持轻量级||**`filter`**|数据清洗(如去空、去噪)|在Sink前减少无效数据写入||**`name`**|运维监控(WebUI显示)|为每个算子设置有意义名称||**`uid`**|状态恢复(Checkpoint/Savepoint)|**必须为有状态算子设置唯一ID**||**`parallelism`**|资源分配(调整并发能力)|根据下游系统(如StarRocks)承受能力设置|>**注意**:在从Kafka到StarRocks的流水线中,`filter`和`map`通常是无状态的,但若涉及状态操作(如`keyBy`后的聚合),则必须设置`uid`[^3]。###常见问题1.**为什么Sink需要设置`uid`?**Sink可能是有状态的(如Exactly-Once语义下的两阶段提交),不设置`uid`会导致作业重启后无法恢复事务状态。2.**如何确定并行度?**-KafkaSource:**与Kafka分区数一致**(最佳性能)。-Transformation:根据数据复杂度调整(通常与Source相同)。-Sink:参考下游系统(如StarRocks)的写入能力,避免压垮集群。3.**`name`和`uid`的区别?**`name`是给人看的(用于监控),`uid`是给Flink系统看的(用于状态管理)。二者可相同,但`uid`必须全局唯一且稳定。§§相关问题§§1.在Flink中如何为有状态算子(如`keyBy`后的窗口)设置UID?2.如果Kafka分区数是12,但Sink并行度设置为8,会有什么影响?3.StarRocksSink如何实现Exactly-Once语义?</think>### Flink DataStream API核心转换操作详解 #### 一、DataStream转换操作基础 在Flink流处理中,DataStream API的核心操作分为三类: 1. **Source**:数据来源(如Kafka) 2. **Transformation**:数据转换(map、filter等) 3. **Sink**:数据输出(如StarRocks) ```mermaid graph LR A[Kafka Source] --> B[map转换] B --> C[filter过滤] C --> D[StarRocks Sink] ``` #### 二、核心转换方法解析 1. **map方法** - **作用**:对数据流中的每个元素进行一对一转换 - **示例**:将Kafka消息转换为目标格式 ```java DataStream<User> userStream = kafkaSource .map(record -> { // 解析JSON字符串为User对象 return parseJson(record); }) .name("json_parser"); // 设置算子名称 ``` 2. **filter方法** - **作用**:过滤不符合条件的数据元素 - **示例**:过滤无效用户记录 ```java DataStream<User> validUsers = userStream .filter(user -> user.isValid()) .uid("user_filter"); // 设置唯一标识 ``` #### 三、算子配置参数详解 在您的代码片段中: ```java map_filter_return.addSink(starRocksSink) .name("sink_starrocks") // 算子名称 .uid("sink_starrocks") // 唯一标识符 .setParallelism(outp); // 并行度设置 ``` 1. **`.name()` 方法** - **作用**:为算子设置可读名称 - **重要性**: - 在Flink Web UI中显示易识别的名称 - 便于监控和日志追踪 - 示例:`name("sink_starrocks")`使监控界面清晰显示StarRocks输出点 2. **`.uid()` 方法** [^3] - **作用**:设置算子唯一标识符 - **关键价值**: - 保证状态一致性:作业重启时精确恢复状态 - 资源管理:精确控制算子资源分配 - 必须为每个有状态算子设置唯一UID - **最佳实践**:保持UID稳定,避免作业升级时状态丢失 3. **`.setParallelism()` 方法** - **作用**:设置算子并行度 - **配置策略**: ```java .setParallelism(outp); // outp为并行度参数 ``` - **并行度决策因素**: - 下游系统承载能力(StarRocks写入性能) - 数据流量大小 - 可用计算资源 - **Kafka最佳实践**:通常与Kafka分区数保持一致[^2] #### 四、完整数据处理流程示例 结合Kafka数据源和StarRocks Sink的典型流程: ```java // 1. 创建Kafka源(引用[2]) FlinkKafkaConsumer<String> kafkaSource = new FlinkKafkaConsumer<>( "input_topic", new SimpleStringSchema(), kafkaProps ).uid("kafka_source"); // 2. 转换处理链 DataStream<AnalyticsResult> processedStream = env .addSource(kafkaSource) .map(record -> parseRecord(record)) // 数据解析 .name("data_parser") .filter(result -> isValid(result)) // 数据过滤 .uid("data_filter") .setParallelism(4); // 转换并行度 // 3. 输出到StarRocks(您的代码) SinkFunction starRocksSink = StarRocksBuilder.buildSinkForCluster(parameterTool); processedStream.addSink(starRocksSink) .name("sink_starrocks") .uid("sink_starrocks") .setParallelism(3); // 根据StarRocks集群能力设置 ``` #### 五、关键配置的作用对比 | 配置项 | 主要作用 | 是否必需 | 最佳实践 | |---------------|-----------------------------------|----------|-----------------------------------| | **name()** | 监控识别和日志追踪 | 推荐 | 使用有意义的业务名称 | | **uid()** | 状态恢复和资源管理[^3] | 必需 | 每个算子唯一且稳定 | | **parallelism**| 控制并发度和资源分配 | 可选 | 匹配下游系统承载能力 | #### 六、生产环境注意事项 1. **UID管理**:作业升级时保持相同UID可继续使用原有状态 2. **并行度优化**: - Kafka Source并行度 ≈ Kafka分区数 - StarRocks Sink并行度 ≤ StarRocks集群最大连接数 3. **故障恢复**:正确设置UID后,Flink可从检查点恢复精确状态 > **关键提示**:对于有状态的Sink(如支持Exactly-Once的StarRocks连接器),必须设置`uid()`以保证故障恢复时的事务一致性[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值