代码之家  ›  专栏  ›  技术社区  ›  Sebastiaan van den Broek

这种在springboot应用程序中启动无限循环的方式有什么问题吗?

  •  4
  • Sebastiaan van den Broek  · 技术社区  · 7 年前

    我有一个springboot应用程序,它需要处理一些Kafka流数据。我在一个 CommandLineRunner Runtime.getRuntime().addShutdownHook(new Thread(consumer::wakeup)); . 我会遇到什么问题吗?在春天有没有一种更惯用的方法来做这个呢?我应该用吗 @Scheduled

    import org.apache.kafka.clients.consumer.Consumer;
    import org.apache.kafka.clients.consumer.ConsumerRecords;
    import org.apache.kafka.clients.consumer.KafkaConsumer;
    import org.apache.kafka.common.errors.WakeupException;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.stereotype.Component;
    
    import java.time.Duration;
    import java.util.Properties;
    
    
        @Component
        public class InfiniteLoopStarter implements CommandLineRunner {
    
            private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
            @Override
            public void run(String... args) {
                Consumer<AccountKey, Account> consumer = new KafkaConsumer<>(new Properties());
                Runtime.getRuntime().addShutdownHook(new Thread(consumer::wakeup));
    
                try {
                    while (true) {
                        ConsumerRecords<AccountKey, Account> records = consumer.poll(Duration.ofSeconds(10L));
                        //process records
                    }
                } catch (WakeupException e) {
                    logger.info("Consumer woken up for exiting.");
                } finally {
                    consumer.close();
                    logger.info("Closed consumer, exiting.");
                }
            }
        }
    
    3 回复  |  直到 7 年前
        1
  •  4
  •   Markoorn    7 年前

    如果你在一个很好的网站上支持它的话,我肯定会支持它的 https://www.baeldung.com/spring-kafka

    您需要以下依赖项:

    <dependency>
        <groupId>org.springframework.kafka</groupId>
        <artifactId>spring-kafka</artifactId>
        <version>2.2.2.RELEASE</version>
    </dependency>
    

    配置很容易添加 @EnableKafka

    @KafkaListener(topics = "topicName")
    public void listenWithHeaders(
      @Payload String message, 
      @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) {
          System.out.println("Received Message: " + message"+ "from partition: " + partition);
    }
    
        2
  •  1
  •   Rishi Saraf    7 年前

    实现看起来不错,但使用CommandLineRunner并不是为此而设计的。CommandLineRunner用于在启动时仅运行一次任务。从设计的角度看,它不太雅致。我宁愿在kafka中使用springintegrationadapter组件。你可以在这里找到例子 https://github.com/raphaelbrugier/spring-integration-kafka-sample/blob/master/src/main/java/com/github/rbrugier/esb/consumer/Consumer.java .

        3
  •  1
  •   Sebastiaan van den Broek    7 年前

    为了回答我自己的问题,我看了一下像springkafka和springcloudstream这样的Kafka集成库,但是与conferent的Schema Registry的集成要么还没有完成,要么我不太清楚。对于基本体来说,这已经足够了,但是对于由模式注册表验证的类型化Avro对象,我们需要它。我现在实现了一个卡夫卡不可知论的解决方案 Spring Boot - Best way to start a background thread on deployment

    @Component
    public class AccountStreamConsumer implements DisposableBean, Runnable {
    
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
        private final AccountService accountService;
        private final KafkaProperties kafkaProperties;
        private final Consumer<AccountKey, Account> consumer;
    
        @Autowired
        public AccountStreamConsumer(AccountService accountService, KafkaProperties kafkaProperties,
                                     ConfluentProperties confluentProperties) {
    
            this.accountService = accountService;
            this.kafkaProperties = kafkaProperties;
    
            if (!kafkaProperties.getEnabled()) {
                consumer = null;
                return;
            }
    
            Properties props = new Properties();
            props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers());
            props.put(AbstractKafkaAvroSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, confluentProperties.getSchemaRegistryUrl());
            props.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, kafkaProperties.getSecurityProtocolConfig());
            props.put(SaslConfigs.SASL_MECHANISM, kafkaProperties.getSaslMechanism());
            props.put(SaslConfigs.SASL_JAAS_CONFIG, PlainLoginModule.class.getName() + " required username=\"" + kafkaProperties.getUsername() + "\" password=\"" + kafkaProperties.getPassword() + "\";");
            props.put(KafkaAvroDeserializerConfig.SPECIFIC_AVRO_READER_CONFIG, true);
            props.put(ConsumerConfig.GROUP_ID_CONFIG, kafkaProperties.getAccountConsumerGroupId());
            props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, KafkaAvroDeserializer.class);
            props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, KafkaAvroDeserializer.class);
    
            consumer = new KafkaConsumer<>(props);
            consumer.subscribe(Collections.singletonList(kafkaProperties.getAccountsTopicName()));
    
            Thread thread = new Thread(this);
            thread.start();
        }
    
        @Override
        public void run() {
            if (!kafkaProperties.getEnabled())
                return;
    
            logger.debug("Started account stream consumer");
            try {
                //noinspection InfiniteLoopStatement
                while (true) {
                    ConsumerRecords<AccountKey, Account> records = consumer.poll(Duration.ofSeconds(10L));
                    List<Account> accounts = new ArrayList<>();
                    records.iterator().forEachRemaining(record -> accounts.add(record.value()));
                    if (accounts.size() != 0)
                        accountService.store(accounts);
                }
            } catch (WakeupException e) {
                logger.info("Account stream consumer woken up for exiting.");
            } finally {
                consumer.close();
            }
        }
    
        @Override
        public void destroy() {
            if (consumer != null)
                consumer.wakeup();
    
            logger.info("Woke up account stream consumer, exiting.");
        }
    }