简体   繁体   English

使用 apache httpclient 和 jetty 容器进行休息调用时无法捕获空闲超时异常

[英]Not able to catch idle timeout exception while making rest call using apache httpclient and jetty container

I have embedded jetty spring boot app.我已经嵌入了码头弹簧启动应用程序。 while making a rest call using apache httpclient lib, the target service to which am making a rest call goes down after establishing connection and then in few milliseconds I can see there is a idle time out exception on console.在使用 apache httpclient lib 进行休息调用时,正在进行休息调用的目标服务在建立连接后关闭,然后在几毫秒内我可以看到控制台上出现空闲超时异常。

I need to catch this exception in code, but am not able to do that.我需要在代码中捕获此异常,但我无法做到这一点。 please help!!!请帮忙!!!

below are the logs下面是日志

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;
        }
 

As this is an idle timeout on the server side, there's nothing you can do on the client side to catch it.由于这是服务器端的空闲超时,因此您无法在客户端执行任何操作来捕获它。

Per the HTTP specs, the server is free to close the connection for any reason.根据 HTTP 规范,服务器可以出于任何原因自由关闭连接。

The client has to make a decision on what do next.客户必须决定下一步做什么。

See https://stackoverflow.com/a/45019073/775715https://stackoverflow.com/a/45019073/775715

It sounds like you have a classic situation where your HTTP response takes longer then your server defined idle timeout.听起来您有一个经典情况,即您的 HTTP 响应时间比您的服务器定义的空闲超时时间更长。

You might be tempted to just make the server idle timeout longer, which will work for a small number of HTTP clients.您可能会想延长服务器空闲超时时间,这将适用于少数 HTTP 客户端。

Wait?等待? Why did I say clients?为什么我说客户? Well, the idle timeout on the client side is often something that you cannot control, such as on a web browser, or a HTTP intermediary (like a corporate proxy, or a mobile network proxy, or load balancer).好吧,客户端的空闲超时通常是您无法控制的,例如在 Web 浏览器或 HTTP 中介(例如公司代理、移动网络代理或负载平衡器)上。 So if you increase the idle timeout on the server, you might start seeing idle timeout failures from different locations now.因此,如果您增加服务器上的空闲超时,您现在可能会开始看到来自不同位置的空闲超时失败。

This means you have to fix things in a dramatic way.这意味着您必须以戏剧性的方式解决问题。

The simplest option is to fix the task on the server side to not take that long to respond.最简单的选择是在服务器端修复任务,不要花那么长时间来响应。

The next option is to have the server side task feed information back to the client as soon as it has information, even if it's incomplete, keeping the connection alive.下一个选项是让服务器端任务在获得信息后立即将信息反馈给客户端,即使信息不完整,保持连接有效。 Don't wait till you have ALL of the information to write to the client.不要等到你有所有的信息写给客户端。

An example would be a long lookup of information, if you have some of the information, write it to the client (in the appropriate content-type/format. partial json, partial xml, etc).一个例子是长时间的信息查找,如果您有一些信息,请将其写入客户端(以适当的内容类型/格式。部分 json、部分 xml 等)。 Then keep doing that until you have the full information and can complete your response.然后继续这样做,直到您拥有完整的信息并可以完成您的回复。

Another common approach is to break up the long running task into multiple requests/response exchanges.另一种常见的方法是将长时间运行的任务分解为多个请求/响应交换。

  1. Your client request the long running task, you get a unique identifier for your results.您的客户请求长期运行的任务,您将获得结果的唯一标识符。
  2. Your client uses this unique identifier over time to make issue more requests for the status of the results.随着时间的推移,您的客户使用这个唯一标识符来发出更多关于结果状态的请求。 Returning status code 204 when there's no content (yet), and 200 when there is content, and 404 if there is no response, and 410 if the unique identifier is no longer valid.当没有内容(还)时返回状态代码 204,有内容时返回 200,如果没有响应则返回 404,如果唯一标识符不再有效则返回 410。

Another common approach is to limit the scope of the task to not allow it to take that long.另一种常见的方法是限制任务的范围,不允许它花费那么长时间。

  1. Your client requests a task that will return LOTS of data.您的客户请求将返回大量数据的任务。
  2. The server responds with a single "page" of (limited) data, along with an identifier to use to request the next "page".服务器以单个“页面”(有限)数据以及用于请求下一个“页面”的标识符进行响应。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM