Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在具有I/O多路复用的Java服务器中异步处理请求?_Java_Multithreading_Sockets - Fatal编程技术网

如何在具有I/O多路复用的Java服务器中异步处理请求?

如何在具有I/O多路复用的Java服务器中异步处理请求?,java,multithreading,sockets,Java,Multithreading,Sockets,假设我正在编写一个Java服务器,它通过TCP/IP与客户机通信 服务器使用I/O多路复用。有一个线程T0,它等待选择器并处理客户端连接。例如,如果连接已准备好读取,则T0将从该连接读取数据 假设服务器已读取传入的请求,现在已准备好处理该请求。由于处理需要时间,请求在另一个线程T1中处理,T0返回等待选择器 假设T1已完成处理并创建响应。现在T0应该开始向客户端连接写入响应。所以我的问题是:T1如何将响应发送到T0 我建议使用只在ServerSocket.accept()上阻塞的服务器线程,一旦

假设我正在编写一个Java服务器,它通过TCP/IP与客户机通信

服务器使用
I/O多路复用
。有一个线程
T0
,它等待
选择器
并处理客户端连接。例如,如果连接已准备好读取,则
T0
将从该连接读取数据

假设服务器已读取传入的请求,现在已准备好处理该请求。由于处理需要时间,请求在另一个线程
T1
中处理,
T0
返回等待选择器


假设
T1
已完成处理并创建响应。现在
T0
应该开始向客户端连接写入响应。所以我的问题是:
T1
如何将响应发送到
T0

我建议使用只在
ServerSocket.accept()
上阻塞的服务器线程,一旦它接受了连接,就将其提交给
ExecutorService
。虽然从理论上讲,您可以有任意数量的线程,但我不会这样做,因为这会使您的应用程序容易受到DoS攻击。相反,限制线程池的最大大小,并在服务器负载超过上限时使其正常降级

ExecutorService
的文档中,实际上有很多关于如何执行此操作的说明

更新:我可能误解了你的问题。正如我现在所理解的,您知道上面建议的解决方案,但希望有目的地使用

这将有助于了解您的服务器提供的服务类型以及可能的限制因素(CPU、磁盘I/O、网络等)


您可以为每个传入连接分配一个唯一的请求ID,并将处理程序对象插入该ID下的映射中。然后,如果连接就绪,网络线程将选择相应的处理程序,并要求它接受一定量的输入/产生一定量的输出。当然,这是否适用于您的情况将取决于您的服务器提供的服务。

相同的线程T1应该读取、处理并将结果返回给客户端

下面是一个关于如何使用JavaNIOAPI而不将线程数量与客户端数量联系起来的概要

**//Thread T0** //wait for selection keys
...
Iterator it = selector.selectedKeys().iterator( );

while (it.hasNext( )) { 
    SelectionKey key = (SelectionKey) it.next();
    // Is a new connection coming in? 
    if (key.isAcceptable( )) {
        ServerSocketChannel server = (ServerSocketChannel) key.channel();
        SocketChannel channel = server.accept()
        // Set the new channel nonblocking
        channel.configureBlocking (false);
        // Register it with the selector
        channel.register (selector, SelectionKey.OP_READ);
    }

    // Is there data to read on this channel?
    if (key.isReadable( )) {
       processRequest (key);
    }

    it.remove( );
}

...

ExecutorService service = Executors.newFixedThreadPool(50);
...
void processRequest(final SelectionKey key) {

    **//Thread T1-T50** //deal with request
    executorService.submit(new Runnable() {            
        SocketChannel channel = (SocketChannel) key.channel();

        //read data from channel, process it and write back to the channel.
    });
)
}

与客户端的所有通信将通过套接字和第二个线程T1进行。T0将仅用于等待新连接。因此,每个客户端连接都有一个线程。您能为1000个客户端提供服务吗?是的,每个客户端连接至少一个线程。关于你的第二个问题,我不做这种类型的编程,所以我不能回答关于1000个客户机的这个特定问题,但我想是这样的,尽管我也认为无论使用何种编程语言,计算机硬件和软件资源都必须受到限制。当然,如果套接字关闭,您会让任何线程(自然)结束。好的。因此,您可能无法为每个连接使用一个(本机)线程来服务1000个客户端。但是,您可以使用I/O多路复用来实现这一点。这就是为什么I/O多路复用是有用的。我将不得不放弃这一点,因为我对多路复用的概念还不是一个完全的新手。我将观看并希望从这个问题线索中学习。谢谢。不幸的是,我认为这个解决方案是次优的。问题是,每个客户端连接有一个线程,然后无法为1000个客户端提供服务器,例如,I/O多路复用(在选择器循环中的线程上)可以提供更多服务。如果您愿意,可以创建一个包含1000个工作线程的线程池。我的意思是,1000个线程对于单个框来说太多了。无论如何,与紧选择器循环中的一个线程相比,每个客户端连接使用一个线程是没有效率的。我同意同步多路复用可能会更有效率。在C中,这将是一条直截了当的道路。另一方面,我认为你不会杀死任何拥有1000个线程的现代机器。(更有可能是1000个套接字…
ExcutorService
s对我来说在Java中感觉更自然。@Michael同样,1000个客户端可以有1个线程池。线程池的大小不是由客户机的数量决定的,事实上,如果客户机有特别苛刻的客户机,那么您可以拥有比客户机更多的线程。谢谢。这与前面建议和讨论的“每个连接的线程”解决方案相同。T1-T50线程仅在客户端发送任何内容时工作。他们不忙着等待套接字上的某些内容。@Michael此解决方案不在每个连接上使用线程,因为执行器的线程可以(并且将)被所有活动连接重用。始终会有x个线程,与当前活动的y连接无关。特别是,x可以是1(
Executors.newSingleThreadExecutor
),您正好拥有您在问题中描述的
T0
T1
。@dcernahoschi谢谢。明白了:)假设一个慢客户端每分钟发送一个字节的10K消息。您是否建议为每个要读取的字节提交一个新任务。看起来开销太大了。@afsantos谢谢你的更正。我会考虑的。