Nginx在处理大请求时,从上游读取响应头时连接被提前关闭

134

我正在使用nginx和node服务器处理更新请求。当我请求一个大数据的更新时,我得到了一个网关超时错误。我从nginx的错误日志中看到了这个错误:

2016/04/07 00:46:04 [error] 28599#0: *1 upstream prematurely closed connection while reading response header from upstream, client: 10.0.2.77, server: gis.oneconcern.com, request: "GET /update_mbtiles/atlas19891018000415 HTTP/1.1", upstream: "http://127.0.0.1:7777/update_mbtiles/atlas19891018000415", host: "gis.oneconcern.com"

我在谷歌上搜索了这个错误并尝试了所有我能想到的事情,但我仍然得到了这个错误。

我的nginx配置有以下代理设置:

    ##
    # Proxy settings
    ##

    proxy_connect_timeout 1000;
    proxy_send_timeout 1000;
    proxy_read_timeout 1000;
    send_timeout 1000;

这是我的服务器配置方式

server {
listen 80;

server_name gis.oneconcern.com;
access_log /home/ubuntu/Tilelive-Server/logs/nginx_access.log;
error_log /home/ubuntu/Tilelive-Server/logs/nginx_error.log;

large_client_header_buffers 8 32k;
location / {
    proxy_pass http://127.0.0.1:7777;
    proxy_redirect off;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $http_host;
    proxy_cache_bypass $http_upgrade;
}

location /faults {
    proxy_pass http://127.0.0.1:8888;
    proxy_http_version 1.1;
    proxy_buffers 8 64k;
    proxy_buffer_size 128k;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}

}

我正在使用Node.js后端在AWS服务器上处理请求。只有当更新需要很长时间(大约3-4分钟)时,才会出现网关错误。对于较小的更新,我没有收到任何错误。非常感谢您提供任何帮助。

Node.js 代码:

app.get("/update_mbtiles/:earthquake", function(req, res){
var earthquake = req.params.earthquake
var command = spawn(__dirname + '/update_mbtiles.sh', [ earthquake, pg_details ]);
//var output  = [];

command.stdout.on('data', function(chunk) {
//    logger.info(chunk.toString());
//     output.push(chunk.toString());
});

command.stderr.on('data', function(chunk) {
  //  logger.error(chunk.toString());
 //   output.push(chunk.toString());
});

command.on('close', function(code) {
    if (code === 0) {
        logger.info("updating mbtiles successful for " + earthquake);
        tilelive_reload_and_switch_source(earthquake);
        res.send("Completed updating!");
    }
    else {
        logger.error("Error occured while updating " + earthquake);
        res.status(500);
        res.send("Error occured while updating " + earthquake);
    }
});
});

function tilelive_reload_and_switch_source(earthquake_unique_id) {
tilelive.load('mbtiles:///'+__dirname+'/mbtiles/tipp_out_'+ earthquake_unique_id + '.mbtiles', function(err, source) {
    if (err) {
        logger.error(err.message);
        throw err;
    }
    sources.set(earthquake_unique_id, source); 
    logger.info('Updated source! New tiles!');
});
}

谢谢你。


1
这个问题本身帮助了我,我在接受 http2 请求时漏掉了 proxy_http_version 1.1; - eigenein
15个回答

44

6
根据NGINX文档,连接超时时间不能超过75秒。“定义与代理服务器建立连接的超时时间。应注意,此超时时间通常不能超过75秒。” - Richard Herries
proxy_read_timeout 300秒; proxy_connect_timeout 75秒;从 Plesk Obsidian 的 logs/proxy_error_log 中修复此错误消息“在从上游读取响应标头时,上游过早关闭了连接”。 - Jan Bludau

30

我认为Nginx的错误提示表明你的nodejs服务器(即“上游”)关闭了连接。你的nodejs配置是怎样的?


3
哦!我发现我的节点服务器在处理大数据请求时会发送空响应。 - Divya Konda
21
您好@DivyaKonda,您能否详细说明一下如何通过提供空响应引起网关超时错误? - Melwyn Furtado
4
也许你的Node.js服务器超时了(默认为2分钟),当超时时,服务器会发送空响应。文档链接:https://nodejs.org/api/http.html#http_server_settimeout_msecs_callback - Steel.Liao
1
嗨 @DivyaKonda,你能解释一下你是怎么修复的吗? - Franva
在我的情况下,我们需要在k8s ingress中使用ssl passthrough,但在nginx ingress控制器(helm chart)中未启用:我们缺少controller.extraArgs.enable-ssl-passthrough: ""。简而言之,任何类型的错误配置都可能导致此问题。 - Joran Dox

9

我曾经遇到过同样的错误,以下是我解决它的方法。

我在服务中声明了以下内容:

Description= Your node service description
After=network.target

[Service]
Type=forking
PIDFile=/tmp/node_pid_name.pid
Restart=on-failure
KillSignal=SIGQUIT
WorkingDirectory=/path/to/node/app/root/directory
ExecStart=/path/to/node /path/to/server.js

[Install]
WantedBy=multi-user.target

这里需要注意的是 "After=network.target"
我在 nginx 方面寻找解决方法了很多天,而问题就在这里。
为了确定这是问题,请按照以下步骤操作:
  1. 停止正在运行的节点服务
  2. 直接在 CLI 中启动 ExecStart 命令
  3. 尝试重现错误。
如果没有弹出,则意味着您的服务存在问题。至少这是我找到答案的方式。 祝大家好运!

2
经进一步调查,看起来那是一个 systemd 配置文件,而 After=network.target 设置试图延迟启动该 nodejs 服务,直到系统的网络已经启动并运行。 - Chris W.

6
我在尝试从Nginx代理服务器下载一个2GB文件时,发现了一个* 145660 upstream prematurely closed connection while reading upstream Nginx错误日志条目。该消息表明“上游”关闭了连接,但实际上与proxy_max_temp_file_size设置有关:

语法:proxy_max_temp_file_size size;
默认值:proxy_max_temp_file_size 1024m;
上下文:http,server,location

启用代理服务器响应缓冲区,并且整个响应不适合由proxy_buffer_size和proxy_buffers指令设置的缓冲区时,响应的一部分可以保存到临时文件中。此指令设置临时文件的最大大小。每次写入临时文件的数据大小由proxy_temp_file_write_size指令设置。

零值禁用将响应缓冲到临时文件中。

此限制不适用于将被缓存或存储在磁盘上的响应。

症状:

  • 下载在大约1GB时被强制停止,
  • Nginx声称上游关闭了连接,但没有代理服务器返回完整内容。

解决方案:

  • 将代理位置的proxy_max_temp_file_size增加到4096m,开始发送完整内容。

6

当我试图将大约50万行数据发送到我的API时,在我的AWS Elastic Beanstalk实例的日志中发现了这个错误。

我遵循了所有建议,但都没有成功。

最终有效的方法是将我的EC2实例的大小从1核心和1GB RAM增加到4核心和8GB RAM。


2
这对我也解决了问题;我正在尝试在一台只有1GB RAM的虚拟机上执行一个复杂的查询。将其提升到2GB是我的解决方案。请检查您的服务器是否具有足够的内存! - Obsidian Age

4

您可以通过以下方式在Node中增加超时时间。

app.post('/slow/request', function(req, res) {
    req.connection.setTimeout(100000); //100 seconds
    ...
}

3

我不认为这是你的情况,但如果有帮助的话,我会发布它。我遇到了同样的问题,问题是Node根本没有响应(当失败时,我没有任何操作-因此没有响应)-所以如果增加所有超时时间都没有解决问题,请确保所有场景都得到响应。


3
我也遇到了这个问题,并找到了这篇文章。最终,这些答案都没有解决我的问题,相反,我不得不添加一个重写规则来剥离location /rt,因为我的开发人员制作的后端并不希望有任何其他路径:
┌─(william@wkstn18)──(Thu, 05 Nov 20)─┐
└─(~)──(16:13)─>wscat -c ws://WebsocketServerHostname/rt
error: Unexpected server response: 502

使用wscat进行测试多次得到502响应。Nginx错误日志提供了与上述相同的上游错误,但请注意上游字符串显示GET请求试图访问localhost:12775/rt而不是localhost:12775:
 2020/11/05 22:13:32 [error] 10175#10175: *7 upstream prematurely closed
 connection while reading response header from upstream, client: WANIP,
 server: WebsocketServerHostname, request: "GET /rt/socket.io/?transport=websocket
 HTTP/1.1", upstream: "http://127.0.0.1:12775/rt/socket.io/?transport=websocket",
 host: "WebsocketServerHostname"

由于开发人员没有编码他们的WebSocket(监听12775端口)以期望/rt/socket.io,而只是/socket.io/(注意:/socket.io/似乎只是一种指定WebSocket传输的方法,在这里讨论)。因此,我没有要求他们重写他们的socket代码,而是像下面这样放置了一个重写规则来将WebsocketServerHostname/rt翻译为WebsocketServerHostname:12775。
upstream websocket-rt {
        ip_hash;

        server 127.0.0.1:12775;
}

server {
        listen 80;
        server_name     WebsocketServerHostname;

        location /rt {
                proxy_http_version 1.1;

                #rewrite /rt/ out of all requests and proxy_pass to 12775
                rewrite /rt/(.*) /$1  break;

                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $host;

                proxy_pass http://websocket-rt;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
        }

}

1
有一种更简单的方法,无需进行任何额外的重写:location /rt/ { ... proxy_pass http://websocket-rt/; }。这样,/rt前缀将自动被剥离,并且不会涉及任何PCRE库调用。而且,从技术上讲,当使用location /rt { ... }时,更正确的重写规则应该是rewrite ^/rt(?:/(.*))? /$1 break;(或者应该使用location /rt/ { ... }来确保斜杠跟随在/rt前缀之后)。 - Ivan Shatsky

1

当你的代码进入循环时,也会出现此错误。因此,请调查是否有任何(间接地)自我引用的代码导致了此问题。


0

我遇到了同样的问题,这里详细介绍的解决方案都没有对我起作用... 首先,我遇到了一个错误413 Entity too large,所以我按照以下方式更新了我的nginx.conf:

http {
        # Increase request size
        client_max_body_size 10m;

        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;
        # server_tokens off;

        # server_names_hash_bucket_size 64;
        # server_name_in_redirect off;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ##
        # SSL Settings
        ##

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        ##
        # Logging Settings
        ##

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        ##
        # Gzip Settings
        ##

        gzip on;

        # gzip_vary on;
        # gzip_proxied any;
        # gzip_comp_level 6;
        # gzip_buffers 16 8k;
        # gzip_http_version 1.1;
        # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

        ##
        # Virtual Host Configs
        ##

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;

        ##
        # Proxy settings
        ##
        proxy_connect_timeout 1000;
        proxy_send_timeout 1000;
        proxy_read_timeout 1000;
        send_timeout 1000;
}

所以我只更新了 HTTP 部分,现在我遇到了 502 Bad Gateway 错误,当我显示 /var/log/nginx/error.log 时,我得到了著名的 "upstream prematurely closed connection while reading response header from upstream" 错误。

对我来说真正神秘的是,当我在我的服务器上使用 virtualenv 运行它并将请求发送到:IP:8000/nameOfTheRequest 时,该请求可以工作。

谢谢阅读。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接