我有一个队列(恰好是Kafka,但我不确定是否重要)我正在阅读消息。我想创建一个流来表示这些数据。
我使用(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();
使用此伪代码,流不会返回直到,而while循环被破坏,即直到所有队列都被读取。
我希望能够立即返回流,即可以在 之后将新消息添加到流。
我觉得我需要使用Stream.generate,但我不确定如何,或者我需要一个分裂器?
我还想在稍后的代码中关闭流。
谢谢!
答案 0 :(得分:2)
以下是一个如何完成的评论示例:
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);
}
// 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();
}
}
此代码背后的想法是,您可以通过自定义Stream
提供Iterator
,自己从外部源提取数据。
数据通过Iterator
从外部来源传输到Queue
。因为只有你知道你的数据是什么样的以及如何检测到没有任何剩余的东西可以读取,所以确定流是否应该继续被馈送的过程被提取到用户提供的函数。
希望有帮助吗?