Jetty HttpClient在启动时启动线程,似乎产生了相当多的线程.我对此感到有些惊讶,因为我认为基于nio的网络的优点之一是通过线程多路复用对话.使用HttpClient.setExecutor(…)来控制线程数很容易,但我不确定如何考虑应该需要多少线程.每个同时的HTTP请求库是否需要一个线程,因为HttpURLConnection可能?它是否在某种程度上复用?
我正在定义一个在我的应用程序中长期存在的API客户端,并且我正在尝试平衡保持较小的占用空间以及实现良好的并发性能.
非常感谢任何见解.
解决方法:
默认情况下,Jetty的HttpClient线程池是Jetty的QueuedThreadPool,因此它确实启动了几个线程.
这些线程用于执行DNS查找(在Java中阻塞,不可能使它们成为非阻塞)并接收响应.
请求可以由应用程序线程和池化线程发送(后者在请求已排队的情况下).
Jetty的HttpClient是完全无阻塞的,因为它基于Jetty NIO库,而后者又基于JDK的NIO库.
因此,每个请求不需要一个线程,并且它能够在很少的线程中复用许多请求/响应.
即使HttpClient在执行网络I / O时完全没有阻塞,也会出现诸如超时和DNS等需要额外线程的事情,这就是您看到其他线程的原因.
如果您需要平衡小占用空间和良好的并发性,那么您必须通过调整传递给HttpClient的执行程序来尝试并找到最佳位置.
调整的另一个重要因素是在HttpClientTransportOverHTTP级别配置的选择器数量,请参见http://www.eclipse.org/jetty/documentation/current/http-client-transport.html#_http_1_1_transport.
什么是正确的线程数没有通用的答案,因为它取决于许多因素.正如我所说,你必须尝试并调整各种参数.
例如,如果目标地址都是众所周知的(例如内部网络),则可以使用以下命令替换默认的异步SocketAddressResolver:
httpClient.setSocketAddressResolver(new SocketAddressResolver.Sync());
这将消除执行DNS查找的额外调度.
对于许多通常低于1-5千的插座,选择器的数量可以保持为1,具体取决于硬件.
HttpClient可用的线程数越少,负载增加时的延迟就越长.
QueuedThreadPool能够根据需要生成新线程,并在不再需要时终止它们,弹性行为(因此通常保留默认配置),但您可以根据具体情况尝试不同的配置.