kafka核心概念:
Kafka 是一个消息队列,生产者向消息队列中写入数据,消费者从队列中获取数据并进行消费。可以认为一个 Topic 就是一个队列,每个 Topic 又会被分成多个 Partition,这样做是为了横向扩展,提高吞吐量。
Kafka 中每个 Partition 都对应一个 Broker,一个 Broker 可以管理多个 Partition。举个例子,假如 Kafka 的某个 Topic 有 10 个 Partition、2 个 Broker,那么每个 Broker 就会管理 5 个 Partition。
我们可以把 Partition 简单理解为一个文件,在接收生产者的数据时,需要将数据动态追加到 Partition 上。
生产者会决定将数据写入哪个 Partition,消费者自己维护消费数据的位置,我们称为 Offset。
flink消费kafka:
pom.xml文件引入
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka_2.11</artifactId>
<version>1.10.0</version>
</dependency>
一、消费单个Topic
FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>(
"test",
new SimpleStringSchema(),
properties);
二、消费多个Topic
List<String> topics = new LinkedList<>();
topics.add("test_A");
topics.add("test_B");
FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>(
topics,
new SimpleStringSchema(),
properties);
三、消息序列化
默认的消息的序列化方式为SimpleStringSchema的时候,返回的结果中只有原数据,没有topic、parition等信息
自定义序列化的方式来实现自定义返回数据的结构:
public class CustomDeSerializationSchema implements KafkaDeserializationSchema<ConsumerRecord<String, String>> {
//是否表示流的最后一条元素,设置为false,表示数据会源源不断的到来
@Override
public boolean isEndOfStream(ConsumerRecord<String, String> nextElement) {
return false;
}
//这里返回一个ConsumerRecord<String,String>类型的数据,除了原数据还包括topic,offset,partition等信息
@Override
public ConsumerRecord<String, String> deserialize(ConsumerRecord<byte[], byte[]> record) throws Exception {
return new ConsumerRecord<String, String>(
record.topic(),
record.partition(),
record.offset(),
new String(record.key()),
new String(record.value())
);
}
//指定数据的输入类型
@Override
public TypeInformation<ConsumerRecord<String, String>> getProducedType() {
return TypeInformation.of(new TypeHint<ConsumerRecord<String, String>>(){});
}
}
四、Parition和Topic动态发现
- Parition动态发现(打开动态分区发现功能)
每隔 10ms 会动态获取 Topic 的元数据,对于新增的 Partition 会自动从最早的位点开始消费数据。防止新增的分区没有被及时发现导致数据丢失,消费者必须要感知 Partition 的动态变化
properties.setProperty(FlinkKafkaConsumerBase.KEY_PARTITION_DISCOVERY_INTERVAL_MILLIS, "10");
- Topic动态发现(指定 Topic 的正则表达式)
FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>(
Pattern.compile("^test_([A-Za-z0-9]*)$"),
new SimpleStringSchema(),
properties);
五、Flink消费Kafka设置offset的方法
- 指定Topic和Partition
Map<KafkaTopicPartition, Long> offsets = new HashMap();
offsets.put(new KafkaTopicPartition("test", 0), 10000L);
offsets.put(new KafkaTopicPartition("test", 1), 20000L);
offsets.put(new KafkaTopicPartition("test", 2), 30000L);
consumer.setStartFromSpecificOffsets(offsets);
- 从最早位点开始消费
consumer.setStartFromEarliest();
- 从指定时间点开始消费
consumer.setStartFromTimestamp(1559801580000l);
- 从最新的数据开始消费
consumer.setStartFromLatest();
- 从上次消费位点开始消费
consumer.setStartFromGroupOffsets();
完成代码:
public class KafkaConsumer {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
env.enableCheckpointing(5000);
Properties properties = new Properties();
properties.setProperty("bootstrap.servers", "127.0.0.1:9092");
//设置消费组
properties.setProperty("group.id", "group_test");
/**
* 打开动态分区发现功能
* 每隔 10ms 会动态获取 Topic 的元数据,对于新增的 Partition 会自动从最早的位点开始消费数据。
* 防止新增的分区没有被及时发现导致数据丢失,消费者必须要感知 Partition 的动态变化
*/
properties.setProperty(FlinkKafkaConsumerBase.KEY_PARTITION_DISCOVERY_INTERVAL_MILLIS, "10");
//动态地发现 Topic,可以指定 Topic 的正则表达式
// FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>(
// Pattern.compile("^test_([A-Za-z0-9]*)$"),
// new SimpleStringSchema(),
// properties);
//消费单个 Topic
//默认的消息的序列化方式为 SimpleStringSchema 的时候,返回的结果中只有原数据,没有 topic、parition 等信息
FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>(
"test",
new SimpleStringSchema(),
properties);
//消费多个 Topic
// List<String> topics = new LinkedList<>();
// topics.add("test_A");
// topics.add("test_B");
// FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>(
// topics,
// new SimpleStringSchema(),
// properties);
//设置从最早的offset消费
consumer.setStartFromEarliest();
env.addSource(consumer).flatMap(new FlatMapFunction<String, String>() {
@Override
public void flatMap(String value, Collector<String> out) throws Exception {
System.out.println(value);
}
});
env.execute("start consumer...");
}
}