【问题标题】:NoHttpResponseException thrown as runtime exception even though is a checked exceptionNoHttpResponseException 作为运行时异常抛出,即使是已检查异常
【发布时间】:2017-10-19 10:41:33
【问题描述】:

我遇到了一个奇怪的问题,即 org.apache.http.NoHttpResponseException 被作为未经检查的异常抛出,即使它是一个检查的异常,因为它扩展了 java.io.IOException ... 从以下发布的堆栈跟踪中可以看出,我得到了一个应该在编译时作为未经检查的运行时异常检查的异常。

我得到的异常的堆栈跟踪如下(我的类在包中:com.example.staticsite):

org.apache.http.NoHttpResponseException: sqs.eu-west-1.amazonaws.com failed to respond
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:143)
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:57)
    at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:260)
    at org.apache.http.impl.AbstractHttpClientConnection.receiveResponseHeader(AbstractHttpClientConnection.java:283)
    at org.apache.http.impl.conn.DefaultClientConnection.receiveResponseHeader(DefaultClientConnection.java:251)
    at org.apache.http.impl.conn.ManagedClientConnectionImpl.receiveResponseHeader(ManagedClientConnectionImpl.java:197)
    at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:271)
    at com.amazonaws.http.protocol.SdkHttpRequestExecutor.doReceiveResponse(SdkHttpRequestExecutor.java:66)
    at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:123)
    at org.apache.http.impl.client.DefaultRequestDirector.tryExecute(DefaultRequestDirector.java:685)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:487)
    at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:863)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
    at com.amazonaws.http.AmazonHttpClient.executeOneRequest(AmazonHttpClient.java:728)
    at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:489)
    at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:310)
    at com.amazonaws.services.sqs.AmazonSQSClient.invoke(AmazonSQSClient.java:2419)
    at com.amazonaws.services.sqs.AmazonSQSClient.receiveMessage(AmazonSQSClient.java:1130)
    at com.example.staticsite.aws.SqsReceiverImpl.receiveReceipt(SqsReceiverImpl.java:57)
    at com.example.staticsite.core.processsite.ProcessSiteImpl.runOneTime(ProcessSiteImpl.java:59)
    at com.example.staticsite.core.processsite.ProcessSiteImpl.run(ProcessSiteImpl.java:44)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:473)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:622)
    at java.lang.Thread.run(Thread.java:748) 

在我的代码中引发异常的方法是:

public class SqsReceiverImpl implements SqsReceiver {
    private AmazonSQS client;
    private String queueUrl;

    @Inject
    public SqsReceiverImpl(AmazonSQS client,@Assisted String queueUrl) {        
        this.client = client;
        this.queueUrl = queueUrl;
    }

    public List<String> receiveReceipt() throws SqsReceiverException {
        if(queueUrl == null)
            throw new SqsReceiverException(SqsReceiverException.MESSAGE_NO_QUEURURL);
        ReceiveMessageRequest request = new ReceiveMessageRequest();
        request.setMaxNumberOfMessages(10);
        request.setQueueUrl(queueUrl);
        request.setWaitTimeSeconds(20);
    
        ReceiveMessageResult results = null;
        try {
            results = client.receiveMessage(request);
        }
        catch(OverLimitException oe){
            throw new SqsReceiverException("OverLimitException thrown");
        }
        catch(AmazonServiceException oe){
            throw new SqsReceiverException("AmazonServiceException thrown");
        }
        catch(AmazonClientException oe){
            throw new SqsReceiverException("AmazonClientException thrown");
        } 

SqsReceiverException 定义如下:

public class SqsReceiverException extends Exception{

    public SqsReceiverException(String messageNoQueururl) {
        super(messageNoQueururl);
    }
    public static final String MESSAGE_NO_QUEURURL = "Queue url not found. Se the queue url";
}

pom 文件的依赖声明如下:

  <dependencies>
      <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.11</version>
       <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-sqs</artifactId>
    <version>1.10.12</version>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.10.19</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>4.0</version>
</dependency>
<dependency>
  <groupId>com.google.inject.extensions</groupId>
  <artifactId>guice-assistedinject</artifactId>
  <version>4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.4</version>
</dependency>
</dependencies>

产生这个结果:

这个异常怎么可能被威胁为未经检查而应该检查呢? 我在这里有什么遗漏吗?

注意

该异常并不总是可重现的,因为它仅在亚马逊服务缺少响应时才会在生产中发生。

更新

我已经验证了堆栈跟踪,直到到达 AmazonHttpClient 类,而这段代码正在捕获 `IOException':

catch (IOException ioe) {
                if (log.isInfoEnabled()) {
                    log.info("Unable to execute HTTP request: " + ioe.getMessage(), ioe);
                }
                captureExceptionMetrics(ioe, awsRequestMetrics);
                awsRequestMetrics.addProperty(AWSRequestID, null);
                AmazonClientException ace = new AmazonClientException(
                        "Unable to execute HTTP request: " + ioe.getMessage(),
                        ioe);
                if (!shouldRetry(request.getOriginalRequest(),
                                p.apacheRequest,
                                ace,
                                p.requestCount,
                                config.getRetryPolicy())) {
                    throw lastReset(ace, request);
                }
                // Cache the retryable exception
                p.retriedException = ace;
            }

而lastReset应该是引发异常的原因,我不明白记录的异常怎么可能是org.apache.http.NoHttpResponseException ...

stacktrace 之前的行总是:

2017-09-15 07:41:39 INFO  AmazonHttpClient:496 - Unable to execute HTTP request: sqs.eu-west-1.amazonaws.com failed to respond

【问题讨论】:

  • 您使用的是哪种类型的 AmazonSqs 客户端?异步版本?
  • 确实很奇怪,因为异常类型也没有改变,它确实只是一个 IOException。这在客户端到亚马逊端运行?
  • 它在 Amazon EC2 实例、Amazon Red hat linux AMI 上运行
  • "而lastReset应该是引发异常的原因,我不明白记录的异常怎么可能是org.apache.http.NoHttpResponseException" >>你能详细说明这个? NoHttpResponseException 是 IOException 所以它被捕获并记录...
  • 异常消息:org.apache.http.NoHttpResponseException: sqs.eu-west-1.amazonaws.com failed to respond 应该是来自代码的The target server failed to respond。该消息是否已更新?

标签: java exception apache-httpclient-4.x amazon-sqs


【解决方案1】:

我猜你是堆栈跟踪格式化的受害者。

当您将lastReset() 指为罪魁祸首时,我认为您是对的。这是您看到throws IOException 从堆栈跟踪中消失的地方。这个方法显然抛出了一个AmazonClientException(运行时异常),原来的NoHttpResponseException“在里面”。

你可以像这样用 sn-p 模拟这个:

throw new AmazonClientException("Oh no!", new NoHttpResponseException("The AWS server doesn't like you"));

如果我将这行代码插入现有的 Java 应用程序(在本例中为 Spring Boot),这就是我在 Eclipse 控制台中看到的内容:

没有AmazonClientException 的迹象!直到,我向右滚动:

亚马逊决定采用未经检查的异常,documented it here。

所以他们确实(我很确定)将您的 IOException 包装在运行时异常中,通过让您“对您处理的错误进行细粒度控制”来“帮助”您,尽管这并不总是很明显。

说了这么多,我可能是错的。如果我是对的,我希望在堆栈顶部看到您的自定义 SqsReceiverException,因为您确实捕获了 AmazonClientException。

如果没有堆栈跟踪之前标准输出的最后几行,很难确定。如果我写的不对,你能把它们贴出来吗?

更新

您使用 (AmazonHttpClient:496) 更新问题的行是打印堆栈跟踪的行。当您将Throwable 传递给log.info() 时,将打印堆栈跟踪。此跟踪记录在您的异常被包装并重新抛出之前被记录。

所以这部分似乎被“吞食”了:

com.amazonaws.AmazonClientException: Unable to execute HTTP request: sqs.us-east-1.amazonaws.com failed to respond
    at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:500)
    at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:310)
    at com.amazonaws.services.sqs.AmazonSQSClient.invoke(AmazonSQSClient.java:2419)
    at com.amazonaws.services.sqs.AmazonSQSClient.receiveMessage(AmazonSQSClient.java:1130)
    at httptest.Main.main(Main.java:32)
Caused by:

我无法与失踪的SqsReceiverException 交谈。但我不认为DefaultRequestDirector.execute() 的签名在说谎,而且我不认为我们正在处理编译器错误。

也许您可以将oe.printStackTrace() 添加到您的catch (AmazonClientException oe) 块中?

最后,我建议使用调试器逐步完成此操作。要模拟您的生产问题,只需在DefaultHttpResponseParser:140 处设置一个断点,并在此行执行后,将i 更改为-1。然后将堆栈一直退回到您的代码。

我还在AmazonHttpClient:971 处设置了一个断点,这样我就可以更改retries 并避免循环四次。

【讨论】:

  • 用总是在堆栈跟踪之前的行更新了最后的问题,在它之前或之后没有其他相关行。我的日志文件中根本没有SqsReceiverException。这是最奇怪的事情。
  • @aleroot 感谢您更新问题。我已经相应地更新了我的答案。
猜你喜欢
  • 1970-01-01
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-21
  • 2013-05-24
  • 2012-07-17
  • 1970-01-01
相关资源
最近更新 更多