为了账号安全,请及时绑定邮箱和手机立即绑定

Apache Flink与Spring Boot集成详解:实时数据库变更处理指南

FLink logo

Apache Flink 是一个用于无界和有界数据流的状态计算的强大框架。结合 Spring Boot 可以提供一个构建数据处理应用程序的稳健解决方案。本文将介绍如何将 Apache Flink 与 Spring Boot 集成在一起,使用 Debezium 的变更数据捕获(CDC)功能来处理实时数据库的变更。

目录
  1. Apache Flink 介绍
  2. 设置项目
  3. 使用 Spring Boot 配置 Flink
  4. 使用 Debezium 来实现 CDC
  5. 使用 Flink 处理流
  6. 管理状态和检查点
  7. 内嵌方式的缺点
  8. 结论
第1节 Apache Flink介绍

Apache Flink(阿帕奇·弗林特)是一个处理无界和有界数据流的状态计算的分布式引擎。它为数据流上的分布式计算提供了数据分布、通信和容错功能。

Flink 的主要特点包括:

  • 一次处理语义
  • 带有状态的流处理
  • 基于事件时间的处理
  • 高度灵活的窗口
  • 通过分布式检查点来保证容错
2. 搭建项目环境

首先,让我们来创建一个包含Flink依赖的Spring Boot项目。在下面的你的build.gradle文件中添加如下所示的内容:

插件配置 {  
    id 'org.springframework.boot' 版本 '2.5.5'  
    id 'io.spring.dependency-management' 版本 '1.0.11.RELEASE'  
    id 'java'  
}  

依赖关系 {  
    // Flink 的依赖  
    添加 'org.apache.flink:flink-java:1.17.1'  
    添加 'org.apache.flink:flink-streaming-java:1.17.1'  
    添加 'org.apache.flink:flink-clients:1.17.1'  
    添加 'com.ververica:flink-connector-postgres-cdc:2.4.0'  
    添加 'org.apache.flink:flink-connector-base:1.17.1'  
    添加 'org.apache.flink:flink-table-api-java-bridge:1.17.1'  

    添加 'org.projectlombok:lombok'  
    注解处理插件 'org.projectlombok:lombok'  
}
3. 在 Spring Boot 中配置 Flink

创建一个配置类来配置 Flink StreamExecutionEnvironment

@Configuration  
public class FlinkConfig {  

    @Bean  
    public StreamExecutionEnvironment streamExecutionEnvironment() {  
        // 创建一个流执行环境对象
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();  
        // 启用检查点保存,设置检查点周期为60000毫秒
        env.enableCheckpointing(60000);   
        // 设置检查点存储位置为文件路径
        env.getCheckpointConfig().setCheckpointStorage("file:///path/to/checkpoint/directory");  
        // 返回流执行环境对象
        return env;  
    }  
}
4. 使用Debezium实现CDC,

Debezium 是一个开源的变更数据捕获工具。让我们用 Debezium 来设置一个 POSTGRE SQL CDC 数据源。

@Component  
public class MySqlCdcSource {    

    @Bean  
    public SourceFunction mysqlSource() {  
        Properties props = new Properties();  
        props.setProperty("snapshot.mode", "never"); // 启动时避免全量扫描  

        return MySqlSource.<String>builder()  
                .hostname(hostname)  
                .port(port)  
                .database(database)  
                .schemaList(schemaList)  
                .tableList(tableList)  
                .username(username)  
                .password(password)  
                .decodingPluginName(decodingPluginName)  
                .deserializer(new JsonDebeziumDeserializationSchema())  
                .debeziumProperties(props)  
                .build();  

    }  
}
5. 使用 Flink 进行流处理

现在来创建一个用于处理CDC事件的Flink任务。

@Component  
@RequiredArgsConstructor  
public class FlinkJobManager {  
    private final StreamExecutionEnvironment env;  
    private final SourceFunction mysqlSource;  

    public void start() throws Exception {  
        DataStream stream = env.addSource(mysqlSource);  

        final OutputTag<String> transactionMasterTag = new OutputTag<String>("transactions"){};  
        final OutputTag<String> usersTag = new OutputTag<String>("users"){};  

        // 根据表名拆分数据流  
        SingleOutputStreamOperator<String> 处理流 = stream  
            .process(new ProcessFunction<String, String>() {  
                @Override  
                public void processElement(String value, Context ctx, Collector<String> out) throws Exception {  
                    ObjectMapper mapper = new ObjectMapper();  
                    JsonNode jsonNode = mapper.readTree(value);  
                    String tableName = jsonNode.get("source").get("table").asText();  

                    if ("transactions".equals(tableName)) {  
                        ctx.output(transactionMasterTag, value);  
                    } else if ("users".equals(tableName)) {  
                        ctx.output(usersTag, value);  
                    }  
                }  
            });  

        // 处理用户流  
        DataStream userStream = stream  
            .getSideOutput(transactionMasterTag)  
            .map(new UserCdcEventMapper());  

        // 处理交易流  
        DataStream transactionStream = stream  
            .getSideOutput(usersTag)  
            .map(new TransactionCdcEventMapper());  

        // 进一步处理和发送...  
        transactionJsonStream.addSink(transactionOpenSearchSink);  
        userJsonStream.addSink(userOpenSearchSink);  

        env.execute("PostgreSQL CDC任务");  
    }  

}  

这里有一个映射器类(Mapper 类)的例子:

    public class UserCdcEventMapper extends RichMapFunction {  
        private transient ObjectMapper objectMapper;  

        @Override  
        public void open(Configuration parameters) {  
            objectMapper = new ObjectMapper();  
            objectMapper.registerModule(new JavaTimeModule());  
        }  

        @Override  
        public User map(String value) throws Exception {  
            JsonNode rootNode = objectMapper.readTree(value);  
            JsonNode after = rootNode.path("after");  
            return objectMapper.treeToValue(after, User.class);  
        }  
    }
6. 管理状态和检查点

Flink的状态和检查点机制对容错性至关重要。已经在配置中启用了Flink的检查点功能。如果要在您的函数中使用有状态的键控,可以这样做:

    public class StatefulMapper extends RichMapFunction {  
        private transient ValueState countState;  
        // 一个用于存储计数的状态变量
        private transient ValueState countState;  

        @Override  
        public void open(Configuration config) {  
            // 创建一个用于描述count状态的描述符
            ValueStateDescriptor descriptor = new ValueStateDescriptor<>("count", Long.class);  
            // 获取count状态
            countState = getRuntimeContext().getState(descriptor);  
        }  

        @Override  
        public EnrichedTransaction map(Transaction transaction) throws Exception {  
            // 获取当前的计数
            Long count = countState.value();  
            // 如果计数为空,则初始化为0
            if (count == null) {  
                count = 0L;  
            }  
            // 计数加1
            count++;  
            // 更新计数状态
            countState.update(count);  
            // 返回一个新的包含计数的事务
            return new EnrichedTransaction(transaction, count);  
        }  
    }
7. 嵌入式方法的弊端

将 Flink 与 Spring Boot 集成虽然这提供了便捷的开发体验,但这种内置的方式也有一些不足:

  1. 资源争夺可能导致性能不可预测。在Spring Boot应用程序中嵌入Flink意味着Flink会与应用程序共享资源。
  2. 这会限制您独立扩展Flink的能力,而不必考虑Spring Boot应用。
  3. 部署一个带有嵌入的Flink作业的Spring Boot应用可能比将标准Flink应用部署到Flink集群中要复杂。
  4. 确保Spring Boot、Flink和其他库之间的版本兼容性可能很困难。
  5. 标准的Flink监控和管理工具在这种嵌入式设置下可能无法正常工作。

对于生产场景,通常来说,最好将您的 Flink 作业从 Spring Boot 应用程序中分离,并在专有的 Flink 集群上运行。

8. 最后的总结.

将 Apache Flink 与 Spring Boot 集成,可以构建强大的实时数据处理应用平台。通过利用 Debezium 的 CDC(变化数据捕获),我们可以实时处理数据库变更,为事件驱动架构和实时分析提供了新的可能。

然而,特别需要注意的是嵌入式方法的缺点,尤其是在实际生产环境。随着需求的增长,特别是资源管理方面的要求,您可能需要迁移到独立的Flink集群环境以便更好地管理资源和扩展。

记住,流处理的世界既广阔又复杂。这份指南仅仅是个开始,在Flink丰富的生态系统中,还有很多值得探索的地方。连接器、状态管理和处理语义等方面,值得进一步挖掘。

看直播愉快!

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消