Java 8流和传入数据(Kafka)

Java 8流和传入数据(Kafka),java,java-8,apache-kafka,java-stream,kafka-consumer-api,Java,Java 8,Apache Kafka,Java Stream,Kafka Consumer Api,我有一个队列(碰巧是卡夫卡,但我不确定这是否重要),我正在从中阅读消息。我想创建一个流来表示这些数据 我使用(Kafka)队列的伪代码如下所示: List<Message> messages = new ArrayList<>(); while (true) { ConsumerRecords<String, Message> records = kafkaConsumer.poll(100); messages.add(recordsTo

我有一个队列(碰巧是卡夫卡,但我不确定这是否重要),我正在从中阅读消息。我想创建一个流来表示这些数据

我使用(Kafka)队列的伪代码如下所示:

List<Message> messages = new ArrayList<>();

while (true) {
    ConsumerRecords<String, Message> records = kafkaConsumer.poll(100);

    messages.add(recordsToMessages(records));

    if (x) {
        break;
    }
}

return messages.stream();
List messages=new ArrayList();
while(true){
消费者记录记录=kafkaConsumer.poll(100);
messages.add(recordsToMessages(records));
if(x){
打破
}
}
返回消息。stream();
使用此伪代码,在中断while循环之前,即在读取所有队列之前,不会返回流

我希望能够立即返回流,即在流返回后可以向流添加新消息

我觉得我需要使用Stream.generate,但我不确定如何使用,或者我需要一个拆分器

我还想在代码的后面一点关闭流


谢谢

下面是一个有评论的例子,说明如何做到这一点:

public static void main(String[] args) {

    LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();

    // Data producer
    Runnable job = () -> {
        // Send data to the stream (could come from your Kafka queue
        ThreadLocalRandom random = ThreadLocalRandom.current();
        for (int i = 0; i < 10; i++) {
            queue.offer(random.nextInt(100));
            delay(random.nextInt(2) + 1);
        }
        // Send the magic signal to stop the stream
        queue.offer(-1);
    };
    Thread thread = new Thread(job);
    thread.start();

    // Define the condition by which the stream knows there is no data left to consume
    // The function returns the next element wrapped in an Optional, or an empty Optional to tell there is no more data to read
    // In this example, the number -1 is the magic signal
    Function<BlockingQueue<Integer>, Optional<Integer>> endingCondition = q -> {
        try {
            Integer element = q.take();
            return element == -1 ? Optional.empty() : Optional.of(element);
        } catch (InterruptedException e) {
            return Optional.empty();
        }
    };
    QueueConsumingIterator<Integer> iterator = new QueueConsumingIterator<>(queue, endingCondition);

    // Construct a Stream on top of our custom queue-consuming Iterator
    Spliterator<Object> spliterator = Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED);
    Stream<Object> stream = StreamSupport.stream(spliterator, false);

    // Use the Stream as usual :)
    stream.map(String::valueOf).forEach(System.out::println);

}
publicstaticvoidmain(字符串[]args){
LinkedBlockingQueue=新建LinkedBlockingQueue();
//数据生产者
可运行作业=()->{
//将数据发送到流(可能来自Kafka队列
ThreadLocalRandom=ThreadLocalRandom.current();
对于(int i=0;i<10;i++){
queue.offer(random.nextInt(100));
延迟(随机。nextInt(2)+1);
}
//发出神奇的信号来阻止水流
排队。报价(-1);
};
线程=新线程(作业);
thread.start();
//定义流知道没有剩余数据可使用的条件
//函数返回下一个元素,该元素以可选形式包装,或以空可选形式包装,以告知没有更多数据可读取
//在本例中,数字-1是魔术信号
函数结束条件=q->{
试一试{
整数元素=q.take();
返回元素==-1?可选.empty():可选.of(元素);
}捕捉(中断异常e){
返回可选的.empty();
}
};
QueueConsumInGitter迭代器=新的QueueConsumInGitter(队列,endingCondition);
//在自定义队列使用迭代器的顶部构造一个流
Spliterator Spliterator=Spliterators.Spliterator未知大小(迭代器,Spliterator.ORDERED);
Stream=StreamSupport.Stream(spliterator,false);
//像往常一样使用流:)
stream.map(String::valueOf).forEach(System.out::println);
}

//这是一个从BlockingQueue获取数据的自定义迭代器。
//数据流结尾的检测取决于用例,所以它被提取为用户提供的函数
例如,您可能希望等待队列中的特定项,或者在某个超时之后考虑队列“死”…
公共静态类QueueConsumIngierator实现迭代器{
私有最终阻塞队列;
私有最终函数队列读取器;
私有可选元素;
私有布尔元素read=false;
公共QueueConsumInGitter(阻塞队列、函数队列读取器){
this.queue=队列;
this.queueReader=queueReader;
}
@凌驾
公共布尔hasNext(){
如果(!this.elementRead){
this.element=this.queueReader.apply(this.queue);
this.elementRead=true;
}
返回此.element.isPresent();
}
@凌驾
公共教育{
if(hasNext()){
this.elementRead=false;
返回此.element.get();
}
抛出新的NoTouchElementException();
}
}
专用静态无效延迟(int超时){
试一试{
时间单位。秒。睡眠(超时);
}捕捉(中断异常e){
e、 printStackTrace();
}
}
此代码背后的思想是,您可以通过自定义的
迭代器
提供信息,迭代器本身从外部源提取数据

数据通过
队列
从外部源传输到
迭代器
。由于只有您知道数据的外观以及如何检测没有剩余数据可读取,因此确定流是否应继续馈送的过程被提取到用户提供的函数


希望这能有所帮助?

你不能使用do-while循环吗?不幸的是,它只会在循环中运行一次。一旦退出while循环,将不会有更多的值添加到流中。非常感谢Olivier。不过这看起来很冗长。这不是一个独特的用例,所以我认为会有eas更好的方式。好吧,API使最常见的用例易于应用:从集合或数组流,从生成的套件中获取N项…
Spliterators
StreamSupport
仍然允许更具体的需求,但我同意,这比我希望的更方便。此外,我使上面的代码非常可配置,但是你可能想简化它,使之适应你的实际需要。
// This is a custom Iterator that takes data from a BlockingQueue.
// Detection of the end of the data stream is use-case-dependant, so it is extracted as a user-provided Function<Queue, Optional>
// For example you may want to wait for a particular item in the queue, or consider the queue "dead"" after a certain timeout...
public static class QueueConsumingIterator<E> implements Iterator<E> {

    private final BlockingQueue<E> queue;
    private final Function<BlockingQueue<E>, Optional<E>> queueReader;
    private Optional<E> element;
    private boolean elementRead = false;

    public QueueConsumingIterator(BlockingQueue<E> queue, Function<BlockingQueue<E>, Optional<E>> queueReader) {
        this.queue = queue;
        this.queueReader = queueReader;
    }

    @Override
    public boolean hasNext() {
        if (!this.elementRead) {
            this.element = this.queueReader.apply(this.queue);
            this.elementRead = true;
        }
        return this.element.isPresent();
    }

    @Override
    public E next() {
        if (hasNext()) {
            this.elementRead = false;
            return this.element.get();
        }
        throw new NoSuchElementException();
    }

}

private static void delay(int timeout) {
    try {
        TimeUnit.SECONDS.sleep(timeout);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}