当服务器认为它响应时,什么会导致对 localhost 的 HTTP GET 超时?

What can cause HTTP GET to localhost to timeout when the server thinks it responded?

我在 AWS Beanstalk (Tomcat 8.5 + Apache httpd) 中有一个 Java 应用程序 运行。

应用在某一时刻调用本地主机上的 REST 端点。

偶尔我会在日志中看到这样的故障:

14:55:45 ... SEVERE: url[http://localhost/detail.api?id=200030599] timing=12.010 ...

这表明我的 CustomRestTemplate 在 12 秒后放弃等待响应。

但是,查看日志中的几行,我看到来自服务端点的日志条目:

{
    "server_ts": "2020-08-19T14:55:33.890Z",
    "remote_ip": "127.0.0.1",
    "local_ip": "127.0.0.1",
    "method": "GET",
    "url": "/detail.api",
    "query_string": "?id=200030599",
    "protocol": "HTTP/1.1",
    "http_status": 200,
    "referer": null,
    "user_agent": "Apache-HttpClient/4.5.2 (Java/1.8.0_252)",
    "time_elapsed": 5,
    "thread_name": "http-nio-8080-exec-20",
    "host": "localhost",
}

这是我的自定义 servlet 记录器,显示 5 毫秒 响应。这是从外部包装的 Servlet 过滤器记录的。

这个问题反复出现,但非常罕见,我无法重现。所以我需要采取一种智力方法...开发一系列假设和测试来反驳每个假设,直到找到正确的假设。

可能的原因有哪些?

到目前为止我尝试了什么

我编写了自定义记录器,因此我可以捕获上面显示的时间。然后我基本上碰壁了,因为超时发生在“服务器”(本地主机端点)发送的响应和客户端读取的响应之间的某个隐藏维度中。

我看到 Apache 日志(来自 elasticbeanstack)也显示了本地请求:

127.0.0.1 (-) - - [19/Aug/2020:14:55:33 +0000] "GET /detail.api?id=200030599 HTTP/1.1" 200 4982 "-" "Apache-HttpClient/4.5.2 (Java/1.8.0_252)"

最后,我能够使用 JMeter 在本地测试中重现该问题。

事实证明“隐藏维度”潜伏在 Servlet 过滤器中:

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    
    long started = System.currentTimeMillis(); 

    chain.doFilter(request, response);
    
    long elapsed = System.currentTimeMillis() - started;
    
    log(request, response, elapsed);

}

log() 调用包含一个同步数据库插入,它会减慢速度并最终在负载下失败。我假设 servlet 引擎保持连接打开直到这个过滤器 returns。现在开始调查以寻找解决方案。