--------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的更多相关文章

  1. 4、flink自定义source、sink

    一.Source 代码地址:https://gitee.com/nltxwz_xxd/abc_bigdata 1.1.flink内置数据源 1.基于文件 env.readTextFile(" ...

  2. Flume自定义Source、Sink和Interceptor(简单功能实现)

    1.Event event是flume传输的最小对象,从source获取数据后会先封装成event,然后将event发送到channel,sink从channel拿event消费. event由头he ...

  3. PHP递归获取二维数组中指定key的值

    $data = [ "resulterrorCode" => 0, "resultraw" => [ "result" => ...

  4. flume组件汇总 source、sink、channel

    Flume Source Source类型 说明 Avro Source 支持Avro协议(实际上是Avro RPC),内置支持 Thrift Source 支持Thrift协议,内置支持 Exec  ...

  5. 【翻译】Flink Table Api & SQL — 自定义 Source & Sink

    本文翻译自官网: User-defined Sources & Sinks  https://ci.apache.org/projects/flink/flink-docs-release-1 ...

  6. Flink在流处理上常见的Source和sink操作

    flink在流处理上的source和在批处理上的source基本一致.大致有4大类 1.基于本地集合的source(Collection-based-source) 2.基于文件的source(Fil ...

  7. FLUME KAFKA SOURCE 和 SINK 使用同一个 TOPIC

    FLUME KAFKA SOURCE 和 SINK 使用同一个 TOPIC 最近做了一个事情,过滤下kakfa中的数据后,做这个就用到了flume,直接使用flume source 和 flume s ...

  8. 如何用Flink把数据sink到kafka多个(成百上千)topic中

    需求与场景 上游某业务数据量特别大,进入到kafka一个topic中(当然了这个topic的partition数必然多,有人肯定疑问为什么非要把如此庞大的数据写入到1个topic里,历史留下的问题,现 ...

  9. Flink自定义Sink

    Flink自定义Sink Flink 自定义Sink,把socket数据流数据转换成对象写入到mysql存储. #创建Student类 public class Student { private i ...

随机推荐

  1. ascii、unicode、utf-8、gbk 区别?

    发展史: https://www.cnblogs.com/houxt/p/11250878.html python2内容进行编码(默认ascii),而python3对内容进行编码的默认为utf-8. ...

  2. ES 调优查询亿级数据毫秒级返回!怎么做到的?--文件系统缓存

    一道面试题的引入: 如果面试的时候碰到这样一个面试题:ElasticSearch(以下简称ES) 在数据量很大的情况下(数十亿级别)如何提高查询效率? 这个问题说白了,就是看你有没有实际用过 ES,因 ...

  3. 常见的 eslint 基本报错信息

    Missing semicolon 缺少分号 Missing space before opening brace 左大括号前缺少空格 Trailing spaces not allowed 不允许尾 ...

  4. Ubuntu shell系统的环境变量

    1.系统环境变量env命令查看 1)利用export命令导出环境变量 export PS1 导出PS1 添加路径 export PATH=$PATH:/home/daokr/myfile $ sudo ...

  5. CF1097D 【Makoto and a Blackboard】

    我们考虑对于一个\(N\),他如果变成了他的约数\(x\),那又会变成一个子问题 我们定义\(F(n, k)\)为n操作k次的期望个数 那么我们有\(F(n, k) =\sum_{x|n} F(x, ...

  6. 创建一个简单tcp服务器需要的流程

    1.socket创建一个套接字 2.bind绑定ip和port 3.listen使套接字变为可以被动链接 4.accept等待客户端的链接 5.recv/send接收发送数据

  7. Javascript正则RegExp对象replace方法替换url参数值

    看别的博客有用eval执行正则表达式的写法, //替换指定传入参数的值,paramName为参数,replaceWith为新值 function replaceParamVal(paramName,r ...

  8. windows上hexo: command not found

    使用hexo写博客已经有好几个月了,今天突然出现hexo: command not found,应该与我白天的时候调一下环境变量等有关.在对应的path添加环境变量,即可解决该问题.我的环境变量路径为 ...

  9. mac使用poetry

    安装 为了防止依赖冲突不推荐使用pip的方式直接安装,当然你也可以这样做 curl -sSL https://raw.githubusercontent.com/sdispater/poetry/ma ...

  10. 如何写一个webService接口

    第一次写接口的时候,感觉太过笼统,压根不知道接口是个什么东东,,后来自己也查了好多资料,才发现其实接口可以就认为是一个方法,自己多写几种以后就会发现挺简单的,自己整理了一下资料,纯属增强自己的记忆,也 ...