Flink 自定义source和sink,获取kafka的key,输出指定key
--------20190905更新-------
沙雕了,可以用 JSONKeyValueDeserializationSchema,接收ObjectNode的数据,如果有key,会放在ObjectNode中
if (record.key() != null) {
node.set("key", mapper.readValue(record.key(), JsonNode.class));
}
if (record.value() != null) {
node.set("value", mapper.readValue(record.value(), JsonNode.class));
}
if (includeMetadata) {
node.putObject("metadata")
.put("offset", record.offset())
.put("topic", record.topic())
.put("partition", record.partition());
}
-------------------
Flink 的 FlinkKafkaConsumer、FlinkKafkaProducer,在消费、生成kafka 数据的时候,不能指定key,又时候,我们又需要这个key。
val kafkaSource = new FlinkKafkaConsumer[ObjectNode]("kafka_demo", new JsonNodeDeserializationSchema(), Common.getProp)
val sink = new FlinkKafkaProducer[String]("kafka_demo_out", new SimpleStringSchema(), Common.getProp)
sink.setWriteTimestampToKafka(true)
env.addSource(kafkaSource)
.map(node => {
node.put("token", System.currentTimeMillis())
node.toString
})
.addSink(sink)
下面通过flink 的自定source、sink 实现,消费、生成kafka 数据的时候,获取数据的key ,和输出不同key的数据
思路: 使用kafka 原生的api,KafkaConsuemr和KafkaProducer 消费、生产kafka的数据,就可以获取到key值
kafka 生产者:
object KafkaKeyMaker {
val topic = "kafka_key"
def main(args: Array[String]): Unit = {
val producer = new KafkaProducer[String, String](Common.getProp)
while (true) {
val map = Map("user"->"venn", "name"->"venn","pass"->System.currentTimeMillis())
val jsonObject: JSONObject = new JSONObject(map)
println(jsonObject.toString())
// key : msgKey + long
val msg = new ProducerRecord[String, String](topic, "msgKey" + System.currentTimeMillis(), jsonObject.toString())
producer.send(msg)
producer.flush()
Thread.sleep(3000)
}
}
}
kafka 消费者:
object KafkaKeyReceive{
val topic = "kafka_key"
def main(args: Array[String]): Unit = {
val consumer = new KafkaConsumer[String, String](Common.getProp)
consumer.subscribe(util.Arrays.asList(topic + "_out"))
while (true) {
val records = consumer.poll(500)
val tmp = records.iterator()
while (tmp.hasNext){
val record = tmp.next()
val key = record.key()
val value = record.value()
println("receive -> key : " + key + ", value : " + value)
}
Thread.sleep(3000)
}
}
}
flink 代码,自定义source、sink
import com.venn.common.Common
import org.apache.flink.api.scala._
import org.apache.flink.configuration.Configuration
import org.apache.flink.streaming.api.functions.sink.{RichSinkFunction, SinkFunction}
import org.apache.flink.streaming.api.functions.source.{RichSourceFunction, SourceFunction}
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.kafka.clients.consumer.KafkaConsumer
import org.apache.kafka.clients.producer.{KafkaProducer, ProducerRecord}
import scala.collection.JavaConversions._
/**
* Created by venn on 19-4-26.
*/
object KafkaSourceKey { def main(args: Array[String]): Unit = {
// environment
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment env.addSource(new RichSourceFunction[String] {
// kafka consumer 对象
var consumer: KafkaConsumer[String, String] = null // 初始化方法
override def open(parameters: Configuration): Unit = {
consumer = new KafkaConsumer[String, String](Common.getProp)
// 订阅topic
val list = List("kafka_key")
consumer.subscribe(list)
}
// 执行方法,拉取数据,获取到的数据,会放到source 的缓冲区
override def run(ctx: SourceFunction.SourceContext[String]): Unit = {
println("run")
while (true) {
val records = consumer.poll(500)
val tmp = records.iterator()
while (tmp.hasNext) {
val record = tmp.next()
val key = record.key()
val value = record.value() ctx.collect("key : " + key + ", value " + value)
}
} } override def cancel(): Unit = { println("cancel")
}
}).map(s => s + "map")
.addSink(new RichSinkFunction[String] {
// kafka producer 对象
var producer: KafkaProducer[String, String] = null // 初始化
override def open(parameters: Configuration): Unit = {
producer = new KafkaProducer[String, String](Common.getProp)
} override def close(): Unit = {
if (producer == null) {
producer.flush()
producer.close()
}
} // 输出数据,每条结果都会执行一次,并发高的时候,可以按需做flush
override def invoke(value: String, context: SinkFunction.Context[_]): Unit = { println("flink : " + value) val msg = new ProducerRecord[String, String]( "kafka_key_out", "key" + System.currentTimeMillis(), value)
producer.send(msg)
producer.flush()
}
})
// execute job
env.execute("KafkaToKafka")
} }
kafka 生产者数据:
{"user" : "venn", "name" : "venn", "pass" : 1561355358148}
{"user" : "venn", "name" : "venn", "pass" : 1561355361271}
{"user" : "venn", "name" : "venn", "pass" : 1561355364276}
{"user" : "venn", "name" : "venn", "pass" : 1561355367279}
{"user" : "venn", "name" : "venn", "pass" : 1561355370283}
flink 输出数据:
run
flink : key : msgKey1561355358180, value {"user" : "venn", "name" : "venn", "pass" : 1561355358148}map
flink : key : msgKey1561355361271, value {"user" : "venn", "name" : "venn", "pass" : 1561355361271}map
flink : key : msgKey1561355364276, value {"user" : "venn", "name" : "venn", "pass" : 1561355364276}map
flink : key : msgKey1561355367279, value {"user" : "venn", "name" : "venn", "pass" : 1561355367279}map
flink : key : msgKey1561355370283, value {"user" : "venn", "name" : "venn", "pass" : 1561355370283}map
flink : key : msgKey1561355373289, value {"user" : "venn", "name" : "venn", "pass" : 1561355373289}map
flink : key : msgKey1561355376293, value {"user" : "venn", "name" : "venn", "pass" : 1561355376293}map
kafka 消费者:
receive -> key : key1561355430411, value : key : msgKey1561355430356, value {"user" : "venn", "name" : "venn", "pass" : 1561355430356}map
receive -> key : key1561355433427, value : key : msgKey1561355433359, value {"user" : "venn", "name" : "venn", "pass" : 1561355433359}map
receive -> key : key1561355436441, value : key : msgKey1561355436364, value {"user" : "venn", "name" : "venn", "pass" : 1561355436364}map
receive -> key : key1561355439456, value : key : msgKey1561355439367, value {"user" : "venn", "name" : "venn", "pass" : 1561355439367}map
receive -> key : key1561355442473, value : key : msgKey1561355442370, value {"user" : "venn", "name" : "venn", "pass" : 1561355442370}map
receive -> key : key1561355445391, value : key : msgKey1561355445374, value {"user" : "venn", "name" : "venn", "pass" : 1561355445374}map
注:这样设计有个问题,没办法做到精确一次:
1、source 的精确一次可以使用kafka 的低级api,每次从指定的offset 读取数据,提交新的offset,然后将当前的offset 存到状态中,这样即使程序失败,重启到上一个checkpoint状态,数据也不会重复。
2、sink 的处理比较麻烦,以官网介绍的 “两段提交”的方法,提交生产者的数据。简单来说,就是每次数据处理完后,需要提交数据到kafka,不做真正的提交,仅写入一些已定义的状态变量,当chckpoint成功时Flink负责提交这些写入,否则就终止取消掉。
参考zhisheng 大佬的 博客 : 《从0到1学习Flink》—— 如何自定义 Data Source ?
《从0到1学习Flink》—— 如何自定义 Data Sink ?
两段提交的一篇翻译: 【译】Flink + Kafka 0.11端到端精确一次处理语义的实现
Flink 自定义source和sink,获取kafka的key,输出指定key的更多相关文章
- 4、flink自定义source、sink
一.Source 代码地址:https://gitee.com/nltxwz_xxd/abc_bigdata 1.1.flink内置数据源 1.基于文件 env.readTextFile(" ...
- Flume自定义Source、Sink和Interceptor(简单功能实现)
1.Event event是flume传输的最小对象,从source获取数据后会先封装成event,然后将event发送到channel,sink从channel拿event消费. event由头he ...
- PHP递归获取二维数组中指定key的值
$data = [ "resulterrorCode" => 0, "resultraw" => [ "result" => ...
- flume组件汇总 source、sink、channel
Flume Source Source类型 说明 Avro Source 支持Avro协议(实际上是Avro RPC),内置支持 Thrift Source 支持Thrift协议,内置支持 Exec ...
- 【翻译】Flink Table Api & SQL — 自定义 Source & Sink
本文翻译自官网: User-defined Sources & Sinks https://ci.apache.org/projects/flink/flink-docs-release-1 ...
- Flink在流处理上常见的Source和sink操作
flink在流处理上的source和在批处理上的source基本一致.大致有4大类 1.基于本地集合的source(Collection-based-source) 2.基于文件的source(Fil ...
- FLUME KAFKA SOURCE 和 SINK 使用同一个 TOPIC
FLUME KAFKA SOURCE 和 SINK 使用同一个 TOPIC 最近做了一个事情,过滤下kakfa中的数据后,做这个就用到了flume,直接使用flume source 和 flume s ...
- 如何用Flink把数据sink到kafka多个(成百上千)topic中
需求与场景 上游某业务数据量特别大,进入到kafka一个topic中(当然了这个topic的partition数必然多,有人肯定疑问为什么非要把如此庞大的数据写入到1个topic里,历史留下的问题,现 ...
- Flink自定义Sink
Flink自定义Sink Flink 自定义Sink,把socket数据流数据转换成对象写入到mysql存储. #创建Student类 public class Student { private i ...
随机推荐
- C#调用一下CMD
C#程序调用CMD执行命令 在windows环境下,命令行程序为cmd.exe,是一个32位的命令行程序,微软Windows系统基于Windows上的命令解释程序,类似于微软的DOS操作系统.输入 ...
- BZOJ 5093: [Lydsy1711月赛]图的价值 第二类斯特林数+NTT
定义有向图的价值为图中每一个点的度数的 \(k\) 次方之和. 求:对于 \(n\) 个点的无向图所有可能情况的图的价值之和. 遇到这种题,八成是每个点单独算贡献,然后累加起来. 我们可以枚举一个点的 ...
- C# 可为空?及(??、?. )
可空类型修饰符(?): 引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空. 例如:string str=null; 是正确的,int i=null; 编译器就会报错. 为了使值类型也 ...
- Linux中三种SCSI target的介绍之SCST
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/scaleqiao/article/deta ...
- Strategic game(树形DP入门)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1054 题目大意:一棵树,要放置哨兵,要求最少放置多少哨兵能监视到所有的结点 题目分析: 放置哨兵无非两 ...
- UOJ272. 【清华集训2016】石家庄的工人阶级队伍比较坚强 [FWT]
UOJ 思路 很容易想到\(O(3^{3m}\log T)\)的暴力大矩乘,显然过不了. 我们分析一下每次转移的性质.题目给的转移方程是填表法,我们试着改成刷表法看看-- 发现好像没啥用. 注意到游戏 ...
- Error instantiating class cn.edu.zju.springmvc.pojo.Items with invalid types () or values (). 报错解决方法
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.Reflecti ...
- jmeter通过ant执行时报错 jmeter.log not found
原因:权限执行不够,改为root用户即可 :sudo su 日志报错如下: test: [jmeter] Executing test plan: /home/ec2-user/jmeterProg ...
- JS-选项卡制作解释部分
<!DOCTYPE html> <html> <head> <meta name="author" content "郭菊锋,7 ...
- Tkinter 之事件绑定
import tkinter as tk window = tk.Tk() # 设置窗口大小 winWidth = 600 winHeight = 400 # 获取屏幕分辨率 screenWidth ...