如何在事件驱动中避免繁忙的 while 循环 Java
How to avoid a busy while loop in event-driven Java
我目前正在按照以下方式开发我的事件驱动 Java 软件(这是我的主要方法的本质):
while(true) {
Event event = eventListener.poll();
if(event != null) {
// do something
} else {
// do nothing as usual, but burn CPU time.
}
}
根据我正在构建的内容,eventListener
可能正在监听外部 websocket,轮询 Redis 通道以获取更新,或者等待来自位于同一个盒子上的另一个进程的消息(也许通过 UDP/TCP/shm).
我的想法是,当 eventListener
返回 null(大部分时间)时,这种繁忙的循环方法浪费了很多 CPU 时间,因为它只是坐在那里旋转。但是,除了每次迭代放置 Thread.sleep
这不是一个很好的解决方案之外,我不知道还有什么其他方法可以实现这种设计。
理想情况下我想要一个方法:
void run(Event event) {
// do something
}
其中 run
在事件命中 eventListener
时被调用。如果没有这样的事件可用,理想情况下,进程应该只是坐在那里闲置。
现在,我知道有 websocket 库实际上可以做到这一点,我想知道的是我怎样才能为自己构建这样的东西并将我的 CPU 从坐在那里浪费自己无所事事中解放出来?
您需要使用 java non-blocking IO and may be some library that supports high level communication via java NIO (for example netty,它支持 HTTP、websockets 和 redis 等 NIO 风格的通信。
下面是 NIO 工作原理的简短说明。您正在寻找的东西是 Selector
。它允许等到 Channel 上的数据(它是文件或网络连接等的抽象)可用。此等待(Selector.select
方法)是阻塞的,当某些数据可用于读取或要写入的输出缓冲区可以获得新数据时,该过程由 OS 恢复。
代码的示意图如下所示:
Selector selector = createSelector();
Channel channel = createChannelForSocket();
SelectionKey key = channel.register(selector);
while(true) {
int readyChannels = selector.select(TIMEOUT);
if(readyChannels == 0) continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
for(SelectionKey key : selectedKeys) {
if (key.isReadable()) {
readDataFromChannel(key.channel())
} else if (key.isWritable()) {
writeDataToChannel(key.channel())
}
}
}
使用 netty,您有更多高级代码,您可以在其中定义 Handler
,它具有类似 void channelRead(ChannelHandlerContext ctx, Object msg)
的方法,这是一种读取事件侦听器,您可以实现它来侦听读取事件。
netty 有一个内置循环,看起来类似于上面的示例,但对于许多事件侦听器,它会将这些事件传播给特定的侦听器。
如果您有兴趣大规模使用事件驱动架构。您可能想要使用强大的 "event bus",例如 Apache Kafka 或 AWS SNS+SQS。为方便起见,您可以使用 kalium.alkal.io。这将无缝处理来自 POJO 或 protobuf 对象的 de/serializing。
kalium.on(Event.class, event -> {
//doSomething with the event
});
我目前正在按照以下方式开发我的事件驱动 Java 软件(这是我的主要方法的本质):
while(true) {
Event event = eventListener.poll();
if(event != null) {
// do something
} else {
// do nothing as usual, but burn CPU time.
}
}
根据我正在构建的内容,eventListener
可能正在监听外部 websocket,轮询 Redis 通道以获取更新,或者等待来自位于同一个盒子上的另一个进程的消息(也许通过 UDP/TCP/shm).
我的想法是,当 eventListener
返回 null(大部分时间)时,这种繁忙的循环方法浪费了很多 CPU 时间,因为它只是坐在那里旋转。但是,除了每次迭代放置 Thread.sleep
这不是一个很好的解决方案之外,我不知道还有什么其他方法可以实现这种设计。
理想情况下我想要一个方法:
void run(Event event) {
// do something
}
其中 run
在事件命中 eventListener
时被调用。如果没有这样的事件可用,理想情况下,进程应该只是坐在那里闲置。
现在,我知道有 websocket 库实际上可以做到这一点,我想知道的是我怎样才能为自己构建这样的东西并将我的 CPU 从坐在那里浪费自己无所事事中解放出来?
您需要使用 java non-blocking IO and may be some library that supports high level communication via java NIO (for example netty,它支持 HTTP、websockets 和 redis 等 NIO 风格的通信。
下面是 NIO 工作原理的简短说明。您正在寻找的东西是 Selector
。它允许等到 Channel 上的数据(它是文件或网络连接等的抽象)可用。此等待(Selector.select
方法)是阻塞的,当某些数据可用于读取或要写入的输出缓冲区可以获得新数据时,该过程由 OS 恢复。
代码的示意图如下所示:
Selector selector = createSelector();
Channel channel = createChannelForSocket();
SelectionKey key = channel.register(selector);
while(true) {
int readyChannels = selector.select(TIMEOUT);
if(readyChannels == 0) continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
for(SelectionKey key : selectedKeys) {
if (key.isReadable()) {
readDataFromChannel(key.channel())
} else if (key.isWritable()) {
writeDataToChannel(key.channel())
}
}
}
使用 netty,您有更多高级代码,您可以在其中定义 Handler
,它具有类似 void channelRead(ChannelHandlerContext ctx, Object msg)
的方法,这是一种读取事件侦听器,您可以实现它来侦听读取事件。
netty 有一个内置循环,看起来类似于上面的示例,但对于许多事件侦听器,它会将这些事件传播给特定的侦听器。
如果您有兴趣大规模使用事件驱动架构。您可能想要使用强大的 "event bus",例如 Apache Kafka 或 AWS SNS+SQS。为方便起见,您可以使用 kalium.alkal.io。这将无缝处理来自 POJO 或 protobuf 对象的 de/serializing。
kalium.on(Event.class, event -> {
//doSomething with the event
});