背景
线上已有的架构是基于 Spark+ClickHouse 实现的,spark 直接把原始数据写入ck,数据量为几万qps。但是线上另外一个业务有百万级别qps,这种架构存储量巨大,所以需要先计算出topurl,之后存到ck进行查询。但是由于spark没有日志时间概念,所以采用flink方案。
Flink配置
env配置
- 并行度(parallsim):根据数据量级进行配置
- 重启策略(RestartStrategy):这里我们选择了fixed-delay的方式,重试5次间隔10秒
- 指定时间为事件时间
- checkpoint相关配置,包括checpoint timeout,两次checkpoint间隔,stateBackend等。
uid和name
- uid:这是算子的唯一id,每个算子都应该有唯一id,特别是有状态的算子,将state映射到正确的算子上。
- name:可视化的名称。
Flink kafkaSource
- kafkaConsumer常规配置:
- bootstrap.servers
- group.id
- auto.offset.reset
- kafka.consumer.group
- 反序列化器SimpleStringSchema
- commit offset方式:
- 如果 checkpoint 关闭,commit offset 要依赖于 kafka 客户端的 auto commit。需设置enable.auto.commit,auto.commit.interval.ms 参数到 consumer properties,就会按固定的时间间隔定期 auto commit offset 到 kafka。
- 这里我们开启了checkpoint,作业消费的 offset 是 Flink 在 state 中自己管理和容错。提交 offset 作为外部进度的监控。此时需要 setCommitOffsetsOnCheckpoints 为 true 来设置当 checkpoint 成功时提交 offset 到 kafka。此时 commit offset 的间隔就取决于 checkpoint 的间隔,所以此时从 kafka 一侧看到的 lag 可能并非完全实时,如果checkpoint 间隔比较长 lag 曲线可能会是一个锯齿状。
- 消费并行度和kafka分区数之间的关系:尽量设置1:1,如果资源有限设置1:2,但是消费并行度不能大于kafka分区数,否则会导致部分消费线程没数据,会发现此时watermark不生成,无法做cp。
Flink kafkaSink
- kafkaSink常规配置:
- bootstrap.servers
- kafka.sink.topic
- 序列化器SimpleStringSchema
- 结合kafka的事务,两阶段提交 linkKafkaProducer.Semantic.EXACTLY_ONCE
- 并行度和kafka分区对应关系:
- 默认使用FlinkFixedPartition,sink task < partition 个数时会有部分 partition 没有数据写入
- partitioner设置为null,使用RoundRobin方式,轮询写入每个partition,每个task需要和所有partition建连接。
Flink topurl执行图
- 从kafka读取数据;日志清洗,字段抽取;分配时间戳和水位线
- 按照domain,url,timestamp对数据流分组;开一个10s的滚动窗口;每个窗口中按照key对value求和
- 按照窗口结束时间分组;计算每个窗口的topurl
- 写入kafka
执行脚本
使用yarn-application模式执行,执行脚本:
ini
./bin/flink run-application -t yarn-application \
-Dtaskmanager.numberOfTaskSlots=10 \
-Dparallelism.default=10 \
-Dyarn.application.name="TopurlTest" \
-Dtaskmanager.memory.managed.size=0 \
-c com.jd.flink.job.TopUrl \
/xxx/flink-start-1.0-SNAPSHOT.jar topurl 10 \
--detached \
更多参数可以参考:
任务停止并且做sp:
bash
/xxx/flink-1.12.0/bin/flink cancel -s hdfs://xxx/flink/savepoint/ id -yid application_id