Flink流式处理百万数据量CSV文件

前言

最近公司让做一个'没有必要'的需求

需求针对的对象

这是同一个csv文件的不同展示形式

  • Excel展示形式
  • 文本展示形式

这个csv文件中可能有数百万条数据

需求

将所有的异常数据检测出来

  • 什么样的数据是异常数据

圈红的数据我手动添加了一个a

原本是数字类型

现在变成了一个字符串类型

那么程序中将字符串类型转换为数字类型的话

就会报错

那这个值就是异常数据

  • 为什么我说是 '没有必要的需求'

    • 百万级别的数据量的csv一般都是由数据库导出来的

    • 数据库的列字段都是定义好的 比如是decimal类型的数据类型 导出来的话 那么也肯定是数字而不会是字符串

    • 而出现数字成字符串 是由于人工手动录入的情况下 才会出现 而这种情况又比较少

    • 该csv数据集用于跑python算法 比如通过pandas读取csv 统计某一个列数据的和 若全是数字则可以统计 若某一行数据是字符串 则会出现异常 那么可以通过pandas的方式做异常值数据处理 比如剔除这一行即可

  • 综上 花费人力物力去处理这一个没有必要的需求真的有些'没必要'

但领导发话了呀 这是客户的需求

所以do it

大致实现思路

读取该csv文件

解析csv每一行数据

检验每一行数据是否是异常数据

实现方式

  • 普通方式

通过java读取csv 然后一行一行处理

这种方式 若单机内存太小 很容易造成内存溢出

而且方式很low 没有多大挑战性

对个人技术能力没有提升

所以这种方式pass

  • Flink 流式处理

刚好头段时间 自己学习到了Flink

之前一直是纸上谈兵

现在终于有了用武之地

选好了技术方案 let's do it!

业务逻辑图

接下来简要说说此流程上的核心技术的实现原理

rabbitmq

DEMO源码

https://gitee.com/pingfanrenbiji/resource/tree/master/flink/code/rabbitmq

发送消息

重点配置说明

  • durable

    是否持久化,是否将队列持久化到mnesia数据库中,有专门的表保存队列声明

  • exclusive

    ①当前定义的队列是connection的channel是共享的,其他的connection是访问不到的

    ②当connection关闭的时候,队列将被删除

  • autoDelete

    当最后一个consumer(消费者)断开之后,队列将自动删除

监听消息

方式一

重要参数说明

  • autoack
autoAck(同no-ack)为true的时候
消息发送到操作系统的套接字缓冲区时即任务消息已经被消费
但如果此时套接字缓冲区崩溃
消息在未被消费者应用程序消费的情况下就被队列删除

所以,如果想要保证消息可靠的达到消费者端
建议将autoAck字段设置为false
这样当上面套接字缓冲区崩溃的情况同样出现
仍然能保证消息被重新消费
方式二 注解方式
  • 对类添加@RabbitListener(queues = "${java.flink.queue}")注解

    • 指定队列名称 可从配置文件中读取
  • 对方法添加 @RabbitHandler 注解

三个参数

  • Object message
任意类型的消息

# 解析mq消息
String messageString=JsonUtils.toJson(message);
Message message1=JsonUtils.fromJsonObject(messageString,Message.class);
String message2 = new String(message1.getBody(), "UTF-8");
  • Message msg
手动确认

//如果手动ACK,消息会被监听消费,但是消息在队列中依旧存在,如果 未配置 acknowledge-mode 默认是会在消费完毕后自动ACK掉
final long deliveryTag = msg.getMessageProperties().getDeliveryTag();

//通知 MQ 消息已被成功消费,可以ACK了
channel.basicAck(deliveryTag, false);

  • Channel channel
// 处理失败,重新压入MQ
channel.basicRecover();

线程池

源码

https://gitee.com/pingfanrenbiji/resource/tree/master/flink/code/thread

spring线程相关注解

  • @EnableAsync

    使用多线程

  • @Async

加在线程任务的方法上(需要异步执行的任务)
定义一个线程任务
通过spring提供的ThreadPoolTaskExecutor就可以使用线程池

重要参数

  • corePoolSize

    核心线程数

  • maxPoolSize

    最大线程数

  • queueCapacity

    队列容量

  • keepAliveSeconds

    活跃时间

  • waitForTasksToCompleteOnShutdown

    设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean

  • rejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy())

    • setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务

    • CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行

使用线程池中的线程去执行异步任务

分布式内存文件系统Alluxio

环境搭建

  • 自定义dokcer网络
docker network create alluxio_nw
  • 安装alluxio master
docker run -d  --rm \
    -p 19999:19999 \
    -p 19998:19998 \
    --net=alluxio_nw \
    --name=alluxio-master \
    -e ALLUXIO_JAVA_OPTS=" \
       -Dalluxio.master.hostname=alluxio-master"
 \
    alluxio/alluxio master
  • 安装alluxio worker
docker run -d --rm \
    -p 29999:29999 \
    -p 30000:30000 \
    --net=alluxio_nw \
    --name=alluxio-worker \
    --shm-size=3971.64MB \
    -e ALLUXIO_JAVA_OPTS=" \
       -Dalluxio.worker.memory.size=3971.64MB \
       -Dalluxio.master.hostname=alluxio-master \
       -Dalluxio.worker.hostname=alluxio-worker"
 \
    alluxio/alluxio worker

域名转发配置

sudo vim /etc/hosts

127.0.0.1 alluxio-worker

上传alluxio文件

下载alluxio文件

将文件流写入本地

源码

https://gitee.com/pingfanrenbiji/resource/tree/master/flink/code/alluxio

Flink流式处理数据

结合当前业务梳理流程

来源数据源:数百万数据量的CSV文件
结果保存数据:CSV或Mysql

读取目标数据

  • 略过表头

  • 在已知几列的情况下 执行上图代码

    比如有6列
    那么读取csv的时候 
    flink均认为是String类型去读取(人为指定的类型)

筛选异常数据

异常数据的判断标准

比如输入数据源CSV中一行数据为

若认定圈红的那一列是数字类型

那么此时因为是字符串 无法转换为数字类型

那么这一行是异常数据

将异常数据保存

根据业务灵活处理

  • 第一个全红的 2: 表示第二行

  • 第二个圈红的部分 表示 当前列数据应为Double类型但实际上是非数字类型 所以该行是异常数据

在方法内部对于全局变量的使用仅限于在方法内部使用 不会对方法之后的作用域有效

比如

过滤函数

filter 是过滤函数 
对每一行数据进行过滤
返回false则会被过滤掉

全局变量

List<Integer> rowList=new ArrayList<>();
在filter函数作用域之外
可以在filter函数中使用
仅限于filter函数之间才有效
在filter函数之后 则无法使用filter对该变量的修改
  • 保存到CSV
  • 缺陷
需要指定Tuple类

比如生成的csv文件一行有25列 那么就使用Tuple25

还需要定义25个泛型 比如Tuple25<String,String,....>

最多可支持25列

如果是超过25列那么就不支持

所以使用起来非常不方便 而且使用范围有限

我当时在这块费了时间,因为csv列数超过了25列 比如26列,我想着在增加一个Tuple26或TupleN 尝试了之后 不可以 后来找到了国内Flink钉钉群 请教了下里面的大佬 说是建议保存到Mysql中

  • 保存到Mysql
配置mysql信息和要执行的sql语句

局限性

假如我有1000个列 那么需要建立一个表有1000个列吗

如果有5000个列呢 所以这种方式 也不太好

此时已经到了项目的最后期限了 很多同事在等着我的结果 我的压力也倍增 差点准备放弃flink 用low的方式实现 最后灵机一动看到了保存到txt文本文件的方法

  • 保存到Text
这种方式简单有效

DEMO源码

https://gitee.com/pingfanrenbiji/resource/tree/master/flink/code/flink

Flink国内钉钉群号

群号 : 23138101

后记

上面这点东西 忙活了我3-4天时间 

自我感觉 真是太笨了

国内相关的资料目前还比较少

写下这点心得和经验给需要的朋友们

避免走弯路
Flink 是一个流式处理框架,可用于构建和运行高性能、可靠且可伸缩的流式应用程序。它提供了一种以事件时间处理数据的方式,支持低延迟和高吞吐量的处理能力。 Flink流式处理模型基于无界数据流,在输入数据不断到达的过程中进行实时计算和处理。它能够处理无限量的数据,并且能够以低延迟的方式对数据进行处理Flink 通过流处理 API 和数据流图的概念来描述流式处理应用程序。数据流图是由一个或多个算子组成的有向无环图,通过这些算子对数据进行转换和处理。数据以事件的形式进行处理,每个事件都会被传递给相应的算子,然后根据指定的逻辑进行处理,最终输出结果。 Flink 提供了丰富的算子库,包括各种转换、聚合和窗口操作,以满足不同场景下的需求。它还支持事件时间处理,可以处理乱序的事件,并根据事件时间进行窗口操作,保证数据处理的准确性。 除了流处理Flink 还支持批处理,可以将批处理和流处理无缝集成在一个应用程序中。这使得开发人员可以在同一个框架下进行各种类型的数据处理,极大地简化了应用程序的开发和维护。 总之,Flink 是一个功能强大而灵活的流式处理框架,能够满足各种实时数据处理的需求,并且具有良好的可扩展性和容错性。它是构建实时大数据应用程序的理想选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值