flink读取kafka中的数据的所有信息

1. pom文件依赖

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <scala.version>2.11.8</scala.version>
    <flink.version>1.9.0</flink.version>
    <kafka.version>1.0.2</kafka.version>
</properties>

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.scala-lang</groupId>
        <artifactId>scala-library</artifactId>
        <version>${scala.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-streaming-scala_2.11</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-scala_2.11</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-connector-kafka_2.11</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.kafka</groupId>
        <artifactId>kafka-clients</artifactId>
        <version>${kafka.version}</version>
    </dependency>
    <!--slf4j-->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.9</version>
    </dependency>
</dependencies>

2. 写入数据到kafka代码

def main(args: Array[String]): Unit = {
  val properties = new Properties()
  properties.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, classOf[StringSerializer].getName)
  properties.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, classOf[StringSerializer].getName)
  properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "1.2.3.4:9092")
  properties.setProperty(ProducerConfig.CLIENT_ID_CONFIG, "producer-1")
  properties.setProperty(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG, 90000000.toString)
  val producer = new KafkaProducer[String, String](properties)
  val threadPool = new ScheduledThreadPoolExecutor(
    //该参数为核心线程数
    4,
    //该参数为org.apache.commons.lang3.concurrent中的工厂实现类
    new BasicThreadFactory.Builder().namingPattern("thread-call-runner-%d").daemon(true).build())
  for (i <- 0 to 3) {
    threadPool.submit(new Runnable {
      override def run(): Unit = {
        while (true) {
          producer.send(new ProducerRecord[String, String]("test_wzq", i, i + "号分区key", i + "-" + UUID.randomUUID().toString))
          Thread.sleep(4000)
        }
      }
    })
  }
  Thread.sleep(Long.MaxValue)
}

将上述代码中的IP地址和主题名修改为自己的即可,主题分区数为4,为了后面看到读取不同分区数据的效果。

上面的方法可以一直运行写入数据。


3. 读取value数据

flink官网有提供了kafka的连接器,pom依赖引入为:

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-kafka_2.11</artifactId>
    <version>1.9.0</version>
</dependency>

该连接器支持读取和写入kafka数据,如果是直接读取kafka的message,也就是读取value值,非常简单,代码如下:

def main(args: Array[String]): Unit = {
  import org.apache.flink.api.scala._
  val env = StreamExecutionEnvironment.getExecutionEnvironment
  val properties = new Properties()
  properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "1.2.3.4:9092")
  properties.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest")
  properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, "group-8")
  val source: DataStream[String] = env.addSource(new FlinkKafkaConsumer("test_wzq", new SimpleStringSchema(), properties))
  source.print()
  env.execute()
}

注意代码中的 new SimpleStringSchema() ,这是对于字符串序列化和反序列化的schema,部分源码如下:

/**
 * Very simple serialization schema for strings.
 *
 * <p>By default, the serializer uses "UTF-8" for string/byte conversion.
 */
@PublicEvolving
public class SimpleStringSchema implements DeserializationSchema<String>, SerializationSchema<String>

因此直接使用该反序列化模型,可以读取到kafka消息中的value数据,运行结果如下:

四个并行度输出数据,我电脑是4核,所以默认并行度为4,后面所有读取代码都一样。


4. 读取key-value数据

4.1 使用已提供的反序列化器(不可用)

def main(args: Array[String]): Unit = {
  import org.apache.flink.api.scala._
  val env = StreamExecutionEnvironment.getExecutionEnvironment
  val properties = new Properties()
  properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "dev.cloudiip.bonc.local:9092")
  properties.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest")
  properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, "group-7")
  val source: DataStream[tuple.Tuple2[String, String]] = env.addSource(new FlinkKafkaConsumer("test_wzq", new TypeInformationKeyValueSerializationSchema[String, String](classOf[String], classOf[String], env.getConfig), properties))
  source.print()
  
  env.execute()
}

        注意代码 new TypeInformationKeyValueSerializationSchema[String, String](classOf[String], classOf[String], env.getConfig) ,使用的是连接器里提供的反序列化/序列化模型,部分源码如下:

/**
 * A serialization and deserialization schema for Key Value Pairs that uses Flink's serialization stack to
 * transform typed from and to byte arrays.
 *
 * @param <K> The key type to be serialized.
 * @param <V> The value type to be serialized.
 */
@PublicEvolving
public class TypeInformationKeyValueSerializationSchema<K, V> implements KafkaDeserializationSchema<Tuple2<K, V>>, KeyedSerializationSchema<Tuple2<K, V>>

使用该反序列化模型,返回的数据类型为tuple2,直接运行会报错,如下:

在执行readUnsignedByte函数式报错了,具体原因未知,感兴趣的可以看看源码中的函数体。

4.2 使用自定义反序列化模型

4.2.1 自定义反序列化模型类

/**
 * 自定义kafka反序列化模型,将读取到的kafka record中的key、value信息写入二元组
 *
 * @author wzq
 * @date 2019-09-27
 **/
class KafkaTuple2DeserializationSchema extends KafkaDeserializationSchema[(String, String)] {
  /**
   * 用来判断该元素是否为流中的最后一个元素,如果返回true,则会结束发射数据<br>
   * 直接返回false,使其一直发射数据,毕竟要用于流式处理
   *
   * @param nextElement 下一个反序列化完毕的元素
   * @return
   */
  override def isEndOfStream(nextElement: (String, String)): Boolean = {
    false
  }
  
  /**
   * 反序列化函数,用于将读取到的kafka record封装为需要的对象并返回给flink-source
   *
   * @param record kafka record消息
   * @return 二元组,数据分别为key、value
   */
  override def deserialize(record: ConsumerRecord[Array[Byte], Array[Byte]]): (String, String) = {
    (new String(record.key()), new String(record.value()))
  }
  
  /**
   * 获取该反序列化器泛型的的类型信息
   *
   * @return 该反序列化器泛型的的类型信息
   */
  override def getProducedType: TypeInformation[(String, String)] = {
    TypeInformation.of(classOf[(String, String)])
  }
}

4.2.2 读取kafka

def main(args: Array[String]): Unit = {
  import org.apache.flink.api.scala._
  val env = StreamExecutionEnvironment.getExecutionEnvironment
  val properties = new Properties()
  properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "dev.cloudiip.bonc.local:9092")
  properties.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest")
  properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, "group-6")
  val source: DataStream[(String, String)] =
    env.addSource(new FlinkKafkaConsumer[(String, String)]("test_wzq", new KafkaTuple2DeserializationSchema, properties))
  source.print()
  
  env.execute()
}

运行结果为:

可以看到读取到的数据为二元组。


5. 读取消息所有信息

5.1 自定义反序列化模型类

class KafkaRecord2DeserializationSchema extends KafkaDeserializationSchema[ConsumerRecord[Array[Byte], Array[Byte]]] {
  override def isEndOfStream(nextElement: ConsumerRecord[Array[Byte], Array[Byte]]): Boolean = false
  
  override def deserialize(record: ConsumerRecord[Array[Byte], Array[Byte]]): ConsumerRecord[Array[Byte], Array[Byte]] = {
    record
  }
  
  override def getProducedType: TypeInformation[ConsumerRecord[Array[Byte], Array[Byte]]] = {
    TypeInformation.of(classOf[ConsumerRecord[Array[Byte], Array[Byte]]])
  }
}

5.2 读取kafka

def main(args: Array[String]): Unit = {
  import org.apache.flink.api.scala._
  val env = StreamExecutionEnvironment.getExecutionEnvironment
  val properties = new Properties()
  properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "dev.cloudiip.bonc.local:9092")
  properties.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest")
  properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, "group-5")
  val source: DataStream[ConsumerRecord[Array[Byte], Array[Byte]]] =
    env.addSource(new FlinkKafkaConsumer[ConsumerRecord[Array[Byte], Array[Byte]]]("test_wzq", new KafkaRecord2DeserializationSchema, properties))
  source.print()
  
  env.execute()
}

直接运行结果为:

可以看到通过使用kafka client提供的 ConsumerRecord 类来作为反序列化模型的泛型,就可以直接拿到kafka消息的所有信息。如果业务中需要用到kafka消息的所有信息,则可以使用上面提供的自定义反序列化模型。


6. 读取消息部分信息

如果你只是想读取kafka消息的一部分信息,比如读取key、value、topic等,但是不需要headers等信息,则使用上面的自定义反序列化模型,就会返回很多很多数据,造成资源浪费。

6.1 自定义类

/**
 * @param key       消费记录的key
 * @param value     消费记录的值
 * @param topic     消费记录对应的主题
 * @param partition 消费记录对应主题的分区编号
 * @param offset    消费记录对应的偏移量
 * @author wzq
 * @date 2019-09-27
 **/
case class KafkaConsumerRecord(key: String, value: String, topic: String, partition: Int, offset: Long)

该类用于自定义反序列化模型类的泛型。

直接使用case class,可以使用到其提供的apply、toString等函数,比较方便。

6.2 自定义反序列化模型类

class KafkaRecordDeserializationSchema extends KafkaDeserializationSchema[KafkaConsumerRecord] {
  override def isEndOfStream(nextElement: KafkaConsumerRecord): Boolean = {
    false
  }
  
  override def deserialize(record: ConsumerRecord[Array[Byte], Array[Byte]]): KafkaConsumerRecord = {
    KafkaConsumerRecord(new String(record.key()), new String(record.value()), record.topic(), record.partition(), record.offset())
  }
  
  override def getProducedType: TypeInformation[KafkaConsumerRecord] = {
    TypeInformation.of(classOf[KafkaConsumerRecord])
  }
}

6.3 读取kafka

def main(args: Array[String]): Unit = {
  import org.apache.flink.api.scala._
  val env = StreamExecutionEnvironment.getExecutionEnvironment
  val properties = new Properties()
  properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "dev.cloudiip.bonc.local:9092")
  properties.setProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest")
  properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, "group-5")
  val source: DataStream[KafkaConsumerRecord] =
    env.addSource(new FlinkKafkaConsumer[KafkaConsumerRecord]("test_wzq", new KafkaRecordDeserializationSchema, properties))
  source.print()
  
  env.execute()
}

直接运行结果为:

7. 总结

如果你只是想读取kafka消息中的value值,则直接使用 SimpleStringSchema 字符串反序列化模型即可,如果你想要的获取到更多的信息,则可以使用元组、自定义类、使用 ConsumerRecord[Array[Byte], Array[Byte]] 。

建议根据自己的需求来获取自己需要的信息,虽然使用 ConsumerRecord[Array[Byte], Array[Byte]] 可以获取到消息的所有信息,但是将数据传输给flink source,数据量会很大,造成不必要的资源浪费。

发布了20 篇原创文章 · 获赞 47 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/u012443641/article/details/101541699