java - 使用 apache httpclient 和 jetty 容器进行休息调用时无法捕获空闲超时异常
问题描述
我有嵌入式码头弹簧启动应用程序。在使用 apache httpclient lib 进行休息调用时,正在对其进行休息调用的目标服务在建立连接后关闭,然后在几毫秒内我可以看到控制台上出现空闲超时异常。
我需要在代码中捕获此异常,但无法做到这一点。请帮忙!!!
以下是日志
2020-11-11 07:26:53.208 SocketChannelEndPoint@4107f6f5{/10.192.73.136:37100<->/10.195.165.64:8080,OPEN,fill=FI,flush=-,to=29955/30000}{io=1/1,kio=1,kro=1}->HttpConnection@2419e554[p=HttpParser{s=START,0 of -1},g=HttpGenerator@5a570f7c{s=START}]=>HttpChannelOverHttp@33c85589{s=HttpChannelState@55afe3fc{s=IDLE rs=BLOCKING os=OPEN is=IDLE awp=false se=false i=true al=0},r=1,c=false/false,a=IDLE,uri=null,age=0} idle timeout check, elapsed: 29955 ms, remaining: 45 ms
2020-11-11 07:26:53.253 SocketChannelEndPoint@4107f6f5{/10.192.73.136:37100<->/10.195.165.64:8080,OPEN,fill=FI,flush=-,to=30001/30000}{io=1/1,kio=1,kro=1}->HttpConnection@2419e554[p=HttpParser{s=START,0 of -1},g=HttpGenerator@5a570f7c{s=START}]=>HttpChannelOverHttp@33c85589{s=HttpChannelState@55afe3fc{s=IDLE rs=BLOCKING os=OPEN is=IDLE awp=false se=false i=true al=0},r=1,c=false/false,a=IDLE,uri=null,age=0} idle timeout check, elapsed: 30001 ms, remaining: -1 ms
2020-11-11 07:26:53.253 SocketChannelEndPoint@4107f6f5{/10.192.73.136:37100<->/10.195.165.64:8080,OPEN,fill=FI,flush=-,to=30001/30000}{io=1/1,kio=1,kro=1}->HttpConnection@2419e554[p=HttpParser{s=START,0 of -1},g=HttpGenerator@5a570f7c{s=START}]=>HttpChannelOverHttp@33c85589{s=HttpChannelState@55afe3fc{s=IDLE rs=BLOCKING os=OPEN is=IDLE awp=false se=false i=true al=0},r=1,c=false/false,a=IDLE,uri=null,age=0} idle timeout expired
2020-11-11 07:26:53.254 onFail FillInterest@2bc26da2{AC.ReadCB@2419e554{HttpConnection@2419e554::SocketChannelEndPoint@4107f6f5{/10.192.73.136:37100<->/10.195.165.64:8080,OPEN,fill=FI,flush=-,to=30001/30000}{io=1/1,kio=1,kro=1}->HttpConnection@2419e554[p=HttpParser{s=START,0 of -1},g=HttpGenerator@5a570f7c{s=START}]=>HttpChannelOverHttp@33c85589{s=HttpChannelState@55afe3fc{s=IDLE rs=BLOCKING os=OPEN is=IDLE awp=false se=false i=true al=0},r=1,c=false/false,a=IDLE,uri=null,age=0}}}
java.util.concurrent.TimeoutException: Idle timeout expired: 30001/30000 ms
at org.eclipse.jetty.io.IdleTimeout.checkIdleTimeout(IdleTimeout.java:171) ~[jetty-io-9.4.26.v20200117.jar!/:9.4.26.v20200117]
at org.eclipse.jetty.io.IdleTimeout.idleCheck(IdleTimeout.java:113) ~[jetty-io-9.4.26.v20200117.jar!/:9.4.26.v20200117]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [?:?]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
at java.lang.Thread.run(Thread.java:834) [?:?]
2020-11-11 07:26:53.254 close HttpParser{s=START,0 of -1}
2020-11-11 07:26:53.254 START --> CLOSE
2020-11-11 07:26:53.254 HttpConnection@2419e554::SocketChannelEndPoint@4107f6f5{/10.192.73.136:37100<->/10.195.165.64:8080,OPEN,fill=-,flush=-,to=30002/30000}{io=1/1,kio=1,kro=1}->HttpConnection@2419e554[p=HttpParser{s=CLOSE,0 of -1},g=HttpGenerator@5a570f7c{s=START}]=>HttpChannelOverHttp@33c85589{s=HttpChannelState@55afe3fc{s=IDLE rs=BLOCKING os=OPEN is=IDLE awp=false se=false i=true al=0},r=1,c=false/false,a=IDLE,uri=null,age=0} onFillInterestedFailed {}
2020-11-11 07:26:53.254 shutdownOutput SocketChannelEndPoint@4107f6f5{/10.192.73.136:37100<->/10.195.165.64:8080,OPEN,fill=-,flush=-,to=30002/30000}{io=1/1,kio=1,kro=1}->HttpConnection@2419e554[p=HttpParser{s=CLOSE,0 of -1},g=HttpGenerator@5a570f7c{s=START}]=>HttpChannelOverHttp@33c85589{s=HttpChannelState@55afe3fc{s=IDLE rs=BLOCKING os=OPEN is=IDLE awp=false se=false i=true al=0},r=1,c=false/false,a=IDLE,uri=null,age=0}
2020-11-11 07:26:53.256 fillInterested HttpConnection@2419e554::SocketChannelEndPoint@4107f6f5{/10.192.73.136:37100<->/10.195.165.64:8080,OSHUT,fill=-,flush=-,to=30004/30000}{io=1/1,kio=1,kro=1}->HttpConnection@2419e554[p=HttpParser{s=CLOSE,0 of -1},g=HttpGenerator@5a570f7c{s=START}]=>HttpChannelOverHttp@33c85589{s=HttpChannelState@55afe3fc{s=IDLE rs=BLOCKING os=OPEN is=IDLE awp=false se=false i=true al=0},r=1,c=false/false,a=IDLE,uri=null,age=0}
2020-11-11 07:26:53.256 interested FillInterest@2bc26da2{AC.ReadCB@2419e554{HttpConnection@2419e554::SocketChannelEndPoint@4107f6f5{/10.192.73.136:37100<->/10.195.165.64:8080,OSHUT,fill=FI,flush=-,to=0/30000}{io=1/1,kio=1,kro=1}->HttpConnection@2419e554[p=HttpParser{s=CLOSE,0 of -1},g=HttpGenerator@5a570f7c{s=START}]=>HttpChannelOverHttp@33c85589{s=HttpChannelState@55afe3fc{s=IDLE rs=BLOCKING os=OPEN is=IDLE awp=false se=false i=true al=0},r=1,c=false/false,a=IDLE,uri=null,age=0}}}
2020-11-11 07:26:53.256 changeInterests p=false 1->1 for SocketChannelEndPoint@4107f6f5{/10.192.73.136:37100<->/10.195.165.64:8080,OSHUT,fill=FI,flush=-,to=0/30000}{io=1/1,kio=1,kro=1}->HttpConnection@2419e554[p=HttpParser{s=CLOSE,0 of -1},g=HttpGenerator@5a570f7c{s=START}]=>HttpChannelOverHttp@33c85589{s=HttpChannelState@55afe3fc{s=IDLE rs=BLOCKING os=OPEN is=IDLE awp=false se=false i=true al=0},r=1,c=false/false,a=IDLE,uri=null,age=0}
2020-11-11 07:26:53.256 Queued change org.eclipse.jetty.io.ChannelEndPoint$1@158db25 on ManagedSelector@2935fd2c{STARTED} id=0 keys=2 selected=0 updates=0
2020-11-11 07:26:53.256 Wakeup on submit ManagedSelector@2935fd2c{STARTED} id=0 keys=2 selected=0 updates=1
2020-11-11 07:26:53.257 Selector sun.nio.ch.EPollSelectorImpl@50d0532a woken with none selected
2020-11-11 07:26:53.257 Selector sun.nio.ch.EPollSelectorImpl@50d0532a woken up from select, 0/0/2 selected
2020-11-11 07:26:53.257 Selector sun.nio.ch.EPollSelectorImpl@50d0532a processing 0 keys, 1 updates
2020-11-11 07:26:53.257 updateable 1
2020-11-11 07:26:53.257 ignored: WriteFlusher@5f339f8b{IDLE}->null
java.util.concurrent.TimeoutException: Idle timeout expired: 30001/30000 ms
at org.eclipse.jetty.io.IdleTimeout.checkIdleTimeout(IdleTimeout.java:171) ~[jetty-io-9.4.26.v20200117.jar!/:9.4.26.v20200117]
at org.eclipse.jetty.io.IdleTimeout.idleCheck(IdleTimeout.java:113) ~[jetty-io-9.4.26.v20200117.jar!/:9.4.26.v20200117]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [?:?]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
at java.lang.Thread.run(Thread.java:834) [?:?]
try{
ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy() {
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(NumberFormatException ignore) {
}
}
}
// keep alive for 30 seconds only
return 30 * 1000;
}
};
httpClient = HttpClients.custom().setKeepAliveStrategy(myStrategy).build();
postRequest = new HttpPost(url);
response = httpClient.execute(postRequest);
} catch(TimeoutException e) {
throw e;
} catch(IOException e) {
throw e;
} catch (Exception e) {
throw e;
}
解决方案
由于这是服务器端的空闲超时,因此您无法在客户端执行任何操作来捕获它。
根据 HTTP 规范,服务器可以出于任何原因自由关闭连接。
客户必须决定下一步做什么。
见https://stackoverflow.com/a/45019073/775715
听起来你有一个典型的情况,你的 HTTP 响应需要比你的服务器定义的空闲超时更长的时间。
您可能很想延长服务器空闲超时时间,这将适用于少数 HTTP 客户端。
等待?为什么我说客户?好吧,客户端的空闲超时通常是您无法控制的,例如在 Web 浏览器或 HTTP 中介(如公司代理、移动网络代理或负载平衡器)上。因此,如果您增加服务器上的空闲超时,您现在可能会开始看到来自不同位置的空闲超时失败。
这意味着您必须以戏剧性的方式解决问题。
最简单的选择是修复服务器端的任务,以便不需要那么长时间来响应。
下一个选项是让服务器端任务在收到信息后立即将信息反馈给客户端,即使它不完整,也可以保持连接处于活动状态。不要等到所有信息都写给客户。
一个例子是信息的长查找,如果你有一些信息,把它写到客户端(以适当的内容类型/格式。部分 json,部分 xml 等)。然后继续这样做,直到您获得完整的信息并可以完成您的回复。
另一种常见的方法是将长期运行的任务分解为多个请求/响应交换。
- 您的客户请求长期运行的任务,您将获得结果的唯一标识符。
- 随着时间的推移,您的客户会使用此唯一标识符来发出更多关于结果状态的请求。如果没有内容(还)返回状态码 204,有内容时返回 200,如果没有响应返回 404,如果唯一标识符不再有效则返回 410。
另一种常见的方法是限制任务的范围,不允许它花费那么长时间。
- 您的客户请求一个将返回大量数据的任务。
- 服务器以(有限)数据的单个“页面”以及用于请求下一个“页面”的标识符进行响应。
推荐阅读
- python-3.x - 使用 prefetch_related 优化 Django Queryset 多对多 for 循环
- ruby-on-rails - 无法将图像解析为 URL:# 的未定义方法“to_model”
根据我添加
action_text/trix
到我的 rails 应用程序的文档。当我更新内容时。它向我显示了以下错误。任何想法?Showing /home/dell/code/tm/app/views/active_storage/blobs/_blob.html.erb
- python - 执行 send_key 或 clear() 时出现 InvalidElementStateException
- python - Python/Anaconda 包混乱
- python - 试图创建一个函数来返回字母、大写、小写、数字等的总数,但似乎无法正常工作 - Python
- postgresql - 使用Patroni时如何确定主/副本?
- javascript - 有没有办法在网络推送通知(Service Worker)中播放自定义通知声音?
- javascript - 显示点击按钮的顺序
- powershell - Powershell DateTimePicker 不允许自定义样式/颜色
- javascript - 如何在 node.js 和 SQLite3 中使用 document.querySelector 和 require