java—单线程服务器如何能够通过非阻塞i/o满足多个客户机的需求?

oug3syen  于 2021-08-25  发布在  Java
关注(0)|答案(2)|浏览(465)

在实现服务器时,我们可以将一个客户端请求委托给一个线程。我了解到这种方法的问题是每个线程都有自己的堆栈,这将非常“昂贵”。另一种方法是让服务器成为单线程,并在这个服务器线程上实现所有客户机请求,i/o请求作为非阻塞请求。我的疑问是,如果一个服务器线程同时运行多个客户机请求,那么服务器代码不会为每个客户机请求都有指令指针、一组局部变量、函数调用堆栈,那么这不会像以前那样“昂贵”了。我们到底是如何节约的?。

dsekswqp

dsekswqp1#

我了解到这种方法的问题是每个线程都有自己的堆栈,这将非常“昂贵”。
取决于系统资源的紧张程度。在许多当前体系结构上,每个线程分配的典型jvm堆栈空间默认为1mb,尽管这可以通过 -Xss 命令行参数。jvm有多少可供使用的系统内存以及需要多少线程决定了您是否要为编写服务器单线程而付出高昂的代价。
我的疑问是,如果一个服务器线程同时运行多个客户机请求,那么服务器代码不会为每个客户机请求都有指令指针、一组局部变量、函数调用堆栈,那么这不会像以前一样“昂贵”吗
它当然需要在堆中存储每个请求的上下文信息,但我怀疑,保存为传入连接提供服务所需的变量所需的信息量将远远少于1mb。
和大多数事情一样,当我们试图优化程序时,无论是为了减少内存还是其他系统资源的使用,我们真正要竞争的是代码的复杂性。它更难纠正,也更难维护。尽管线程程序可能非常复杂,但在单个线程中隔离请求处理程序可以使代码非常简单,除非它需要以某种方式与其他请求协调。在大多数情况下,编写高性能单线程服务器比线程版本复杂得多。当然,由于不能使用多个处理器,因此性能也会受到限制。

vjhs03f7

vjhs03f72#

使用非阻塞i/o,单个i/o线程可以处理多个连接。在以下情况下,i/o线程将收到通知:
客户端想要连接
上一轮套接字的写入缓冲区已满时,连接套接字的写入缓冲区有空间。
连接套接字的读取缓冲区具有可用于读取的数据
因此,线程利用事件多路复用来使用选择器并发地为连接提供服务。线程等待选择器的一组选择键,选择键包含已注册事件的状态,您可以将用户数据(如“会话”)附加到选择键。
这里使用的一个非常典型的设计模式是React器模式。
但是,您通常希望防止使用运行时间较长的请求阻塞i/o线程。因此,您将工作卸载到不同的线程池中。然后React器变为促React器模式。
通常,您需要调整i/o线程的数量。因此,您可以有一组并行的i/o线程。
但应用程序中的线程总数应保持有限。
这完全取决于你想要什么。以上是我在hazelcast工作时经常使用的技术。
我不会从头开始写这些逻辑。如果你想利用网络,我会看看netty。它负责大部分的繁重工作,并内置了各种优化功能。
我不能100%确定不写入堆栈的线程是否会实际消耗1mb的物理内存。在linux中,(共享的)零页用于内存分配,因此除非线程堆栈被实际写入,否则不会分配实际的页帧(物理内存);这将触发写入时复制,以实际分配页面帧。除了节省内存外,这还可以防止浪费内存带宽来调零堆栈。线程的内存消耗是一回事;但上下文切换是另一个问题。如果线程比内核多得多,那么上下文切换可能会成为一个真正的性能问题。

相关问题