从kafka消费数据,落地成指定数据格式的文件

背景

从指定的kafka消费数据,落地成指定的数据格式的文件

生产者线程

任务:从kafka中拉取数据并存入线程安全的集合中

  • 从kafka中读取消息,需要用到KafkaConsumer,通过和Producer使用相同的topic约定来消费指定数据。配置消费端consumer需要配置文件properties以及订阅的主题topic,这些在构造线程类时就进行配置。
  • 从kafka中拉取的数据都被存储在consumerRecords中,对于这个类,我认为可以将它看作是一个容器,用于保存在特定主题下的每个分区里的consumerRecord列表。
  • 因为采用多线程的方式进行数据的存储与转存,所以采用阻塞队列这个结构来作为数据临时存储的容器。

主要代码如下:

    private static final Properties props = getProperties();
    private static String topic = "test01";   //消费者主题
    private static LinkedBlockingQueue<String> messageQueue = null;

    /**
     * 线程1:从kafka中拉取数据并存入线程安全的集合
     */
    static class KafkaConsumerThread extends Thread{
        private KafkaConsumer<String, String> kafkaConsumer;
        public KafkaConsumerThread(Properties properties, String topic){
            this.kafkaConsumer = new KafkaConsumer<String, String>(properties);
            this.kafkaConsumer.subscribe(Arrays.asList(topic));
        }

        /**
         * 循环的往阻塞队列里推入数据,当达到队列容量上限的时候,线程停止。
         * 等到另一线程将数据取出并存入文件时,才继续向队列推入数据
         */
        @Override
        public void run() {
            try {
                while(true){
                    //拉取消息, 在这个时间段里拉取,时间到了就立刻返回数据
                    ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(100);
                    //处理消息->存储数据, 得到元素是json对象的queue,需要对每个元素进行处理
                    messageQueue = convertRecordsToQueue(consumerRecords);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    private static LinkedBlockingQueue<String> convertRecordsToQueue(ConsumerRecords records) throws InterruptedException {
        Iterator<ConsumerRecord<String, String>> iterator = records.iterator();
        while(iterator.hasNext()){
            if(messageQueue == null){
                //1000表示阻塞队列数量的上限,后期可以写在配置文件中
                messageQueue = new LinkedBlockingQueue<>(1000);
            }
            //每个consumerRecord.value都是一组数据,依次将数据存入队列中
            ConsumerRecord<String, String> consumerRecord = iterator.next();
            String value = consumerRecord.value();
            messageQueue.put(value);
        }
        return messageQueue;
    }

    /**
     * 消费者配置信息导入
     * @return
     */
    private static Properties getProperties() {
        Properties props = new Properties();
        try{
            props = PropertiesLoaderUtils.loadAllProperties("consumer.properties");
        }catch(IOException e){
            e.printStackTrace();
        }
        return props;
    }

其中consumer.properties如下所示,因为项目要求不高,所以没有重写的配置均为默认值。

bootstrap.servers=ip_host:port
group.id=test
key.deserializer=org.apache.kafka.common.serialization.StringDeserializer
value.deserializer=org.apache.kafka.common.serialization.StringDeserializer

消费者线程

任务:从线程安全集合中取出数据并存入文件

将数据存入文件时需要满足:每5s生成一份数据文件且每份文件中存入的记录数量不能大于200,因此在每一次写入前都进行如下判定:

  • 当文件存在时间小于5s并且存入文件的记录数量小于200时,继续向文件中存储。
  • 当文件存在时间大于5s或者存入文件的记录数量达到200时,跳出内部循环,新建文件。
/**
     * 线程2:从线程安全集合中取出数据并存入文件
     */
    static class FileInThread extends Thread{
        public FileInThread(){}
        @Override
        public void run() {
            int counter;
            while(true){
                counter = 0;
                File file = new File(filePath);
                try(BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                        new FileOutputStream(file, true)))){
                    //while循环判定条件,每次写入文件的时候进行判定
                    //因为我将创建文件的时间写在了文件名中,所以下面获取创建时间的方法是对文件名进行处理得到的
                    while (counter < 200 && (new Date().getTime() - Long.parseLong(file.getAbsolutePath().substring(19, 32)) < 5000)) {
                        if(messageQueue != null) {
                            //messageQueue.take() 获得一个完整的json对象,以String表示
                            JSONObject jsonobj = JSONObject.parseObject(messageQueue.take());
                            //获取相应属性key所对应的值,再将相应的值存入file
                            String fileIn = jsonobj.getString("xxxx") + "\r\n";
                            bw.write(fileIn);
                            counter++;
                        }
                    }
                    bw.flush();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Spark Streaming从Kafka消费数据的步骤如下: 1. 首先需要引入相关依赖包,包括Spark Streaming Core、Kafka和Spark Streaming Kafka等。 2. 创建Spark Streaming Context并设置批处理时间间隔。 3. 创建Kafka参数映射,包括Kafka集群地址、消费者组、消费的主题等。 4. 创建一个DStream,使用KafkaUtils.createDirectStream方法从Kafka消费数据。这个方法返回一个包含每个批次数据的RDD的DStream。 5. 对DStream进行数据处理,包括转换、过滤、聚合等。 6. 启动Spark Streaming应用程序并等待它完。 下面是一个示例代码,演示了如何使用Spark Streaming从Kafka消费数据: ``` import org.apache.spark.streaming._ import org.apache.spark.streaming.kafka._ import org.apache.kafka.common.serialization.StringDeserializer val ssc = new StreamingContext(sparkConf, Seconds(5)) val kafkaParams = Map[String, Object]( "bootstrap.servers" -> "localhost:9092", "key.deserializer" -> classOf[StringDeserializer], "value.deserializer" -> classOf[StringDeserializer], "group.id" -> "my-group", "auto.offset.reset" -> "latest", "enable.auto.commit" -> (false: java.lang.Boolean) ) val topics = Array("my-topic") val stream = KafkaUtils.createDirectStream[String, String]( ssc, PreferConsistent, Subscribe[String, String](topics, kafkaParams) ) stream.map(record => (record.key, record.value)) .filter(_._2.contains("error")) .count() .print() ssc.start() ssc.awaitTermination() ``` 在这个示例中,我们从名为“my-topic”的Kafka主题中消费数据,并在每个批次中对包含“error”字符串的记录进行计数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值