nginx 客户端关闭连接
nginx client closes connection
我有一个完全 docker 化的应用程序:
- nginx 作为代理
- 后端服务器(express.js)
- 一个数据库(mongodb)
- 一个前端服务器(express js)
- 用于日志记录的 goaccess
问题是当我用 POST 请求访问我的后端端点时,响应永远不会发送到客户端。 nginx 记录了 499 代码以及此日志
epoll_wait() reported that client prematurely closed connection, so upstream connection is closed too while sending request to upstream,
客户端就是浏览器,这一点不用怀疑。
在 firefox 中处理 1 分钟后出现错误,在 chrome 中处理 5 分钟后出现错误。据我所知,这些时间与这些浏览器的超时设置相匹配。我可以增加 firefox 的超时时间,但这不是一个可行的解决方案。
当我摆脱代理时,请求完成,客户端在大约 15 分钟内收到响应。所以我认为nginx配置有问题,但我不知道是什么。
到目前为止,我尝试增加您可以想象的所有超时,但这并没有改变任何东西。
我也尝试在 nginx 中设置 proxy_ignore_client_abort
但它对我来说没有用。事实上,nginx 和我的后端之间的连接仍然存在,请求在 15 分钟后完成(nginx 日志中的代码 200)但是 ui 没有更新,因为客户端已经终止了与 nginx 的连接。
我认为浏览器认为nginx死了,因为它没有收到任何数据,所以它关闭了TCP连接。
稍后我会尝试通过在我的网站页面之间切换来处理请求时“刺激”此 TCP 连接(因此浏览器不应关闭连接),但是如果我必须做一些奇怪的事情来获得我的后端结果,这不是一个可行的解决方案。
应该有一种方法可以处理长请求而不会遇到这些浏览器超时,但我不知道如何实现。
任何帮助将不胜感激:)
我的 nginx 配置:
user nginx;
pid /run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
multi_accept on;
worker_connections 65535;
}
http {
charset utf-8;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
log_not_found off;
types_hash_max_size 2048;
types_hash_bucket_size 64;
client_max_body_size 16M;
# mime
include mime.types;
default_type application/octet-stream;
# logging
log_format my_log '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" ';
access_log /var/log/nginx/access.log my_log;
error_log /var/log/nginx/error.log info;
# limits
limit_req_log_level warn;
limit_req_zone $binary_remote_addr zone=main:10m rate=10r/s;
# SSL
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Mozilla Intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
# OCSP
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
resolver_timeout 2s;
# Connection header for WebSocket reverse proxy
map $http_upgrade $connection_upgrade {
default upgrade;
"" close;
}
map $remote_addr $proxy_forwarded_elem {
# IPv4 addresses can be sent as_is
~^[0-9.]+$ "for=$remote_addr";
# IPv6 addresses need to be bracketed and quoted
~^[0-9A-Fa-f:.]+$ "for\"[$remote_addr]\"";
# Unix domain socket names cannot be represented in RFC 7239 syntax
default "for=unknown";
}
map $http_forwarded $proxy_add_forwarded {
# If the incoming Forwarded header is syntactially valid, append to it
"~^(,[ \t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\t \x21\x23-\x5B\x5D-\x7E\x80-\xFF]|\\[\t \x21-\x7E\x80-\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\t \x21\x23-\x5B\x5D-\x7E\x80-\xFF]|\\[\t \x21-\x7E\x80-\xFF])*\"))?)*([ \t]*,([ \t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\t \x21\x23-\x5B\x5D-\x7E\x80-\xFF]|\\[\t \x21-\x7E\x80-\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\t \x21\x23-\x5B\x5D-\x7E\x80-\xFF]|\\[\t \x21-\x7E\x80-\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem";
# Otherwise, replace it
default "$proxy_forwarded_elem";
}
# Load configs
include /etc/nginx/conf.d/localhost.conf;
}
和localhost.conf
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name localhost;
root /usr/share/nginx/html;
ssl_certificate /etc/nginx/live/localhost/cert.pem;
ssl_certificate_key /etc/nginx/live/localhost/key.pem;
include /etc/nginx/conf.d/security.conf;
include /etc/nginx/conf.d/proxy.conf;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log info;
# nginx render files or proxy the request
location / {
try_files $uri @front;
}
location @front {
proxy_pass http://frontend:80;
}
location ^~ /api/v1 {
proxy_read_timeout 30m; # because an inference with SIMP can takes some time
proxy_send_timeout 30m;
proxy_connect_timeout 30m;
proxy_pass http://backend:4000;
}
location = /report.html {
root /usr/share/goaccess/html/;
}
location ^~ /ws {
proxy_pass http://goaccess:7890;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_read_timeout 7d;
proxy_connect_timeout 3600;
}
include /etc/nginx/conf.d/general.conf;
}
编辑:
请求是通过 Angular HttpClient
发送的,也许这个模块是 built 的一种方式,如果在短时间内没有发送响应,我会尝试中止请求对此进行调查。
好的,我想我可以回答我自己的问题。
HTTP 请求不是为长请求设计的。发出请求后,应尽快提供响应。
当你在做一个长时间的工作时,你应该使用 worker 和消息架构(或事件驱动架构)以及像 rabbitmq 或 kafka 这样的工具。您也可以使用轮询(但这不是更有效的解决方案)。
因此,在我的 POST 处理程序中,我应该做的是在数据到达时向我的代理发送一条消息,然后发出适当的响应(比如正在处理请求)。
工作人员订阅一个队列,可以接收之前发送的消息,完成工作,然后回复我的后端。然后我们可以使用 STOMP (websocket) 插件将结果路由到前端。
我有一个完全 docker 化的应用程序:
- nginx 作为代理
- 后端服务器(express.js)
- 一个数据库(mongodb)
- 一个前端服务器(express js)
- 用于日志记录的 goaccess
问题是当我用 POST 请求访问我的后端端点时,响应永远不会发送到客户端。 nginx 记录了 499 代码以及此日志
epoll_wait() reported that client prematurely closed connection, so upstream connection is closed too while sending request to upstream,
客户端就是浏览器,这一点不用怀疑。
在 firefox 中处理 1 分钟后出现错误,在 chrome 中处理 5 分钟后出现错误。据我所知,这些时间与这些浏览器的超时设置相匹配。我可以增加 firefox 的超时时间,但这不是一个可行的解决方案。
当我摆脱代理时,请求完成,客户端在大约 15 分钟内收到响应。所以我认为nginx配置有问题,但我不知道是什么。
到目前为止,我尝试增加您可以想象的所有超时,但这并没有改变任何东西。
我也尝试在 nginx 中设置 proxy_ignore_client_abort
但它对我来说没有用。事实上,nginx 和我的后端之间的连接仍然存在,请求在 15 分钟后完成(nginx 日志中的代码 200)但是 ui 没有更新,因为客户端已经终止了与 nginx 的连接。
我认为浏览器认为nginx死了,因为它没有收到任何数据,所以它关闭了TCP连接。
稍后我会尝试通过在我的网站页面之间切换来处理请求时“刺激”此 TCP 连接(因此浏览器不应关闭连接),但是如果我必须做一些奇怪的事情来获得我的后端结果,这不是一个可行的解决方案。
应该有一种方法可以处理长请求而不会遇到这些浏览器超时,但我不知道如何实现。 任何帮助将不胜感激:)
我的 nginx 配置:
user nginx;
pid /run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
multi_accept on;
worker_connections 65535;
}
http {
charset utf-8;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
log_not_found off;
types_hash_max_size 2048;
types_hash_bucket_size 64;
client_max_body_size 16M;
# mime
include mime.types;
default_type application/octet-stream;
# logging
log_format my_log '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" ';
access_log /var/log/nginx/access.log my_log;
error_log /var/log/nginx/error.log info;
# limits
limit_req_log_level warn;
limit_req_zone $binary_remote_addr zone=main:10m rate=10r/s;
# SSL
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Mozilla Intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
# OCSP
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
resolver_timeout 2s;
# Connection header for WebSocket reverse proxy
map $http_upgrade $connection_upgrade {
default upgrade;
"" close;
}
map $remote_addr $proxy_forwarded_elem {
# IPv4 addresses can be sent as_is
~^[0-9.]+$ "for=$remote_addr";
# IPv6 addresses need to be bracketed and quoted
~^[0-9A-Fa-f:.]+$ "for\"[$remote_addr]\"";
# Unix domain socket names cannot be represented in RFC 7239 syntax
default "for=unknown";
}
map $http_forwarded $proxy_add_forwarded {
# If the incoming Forwarded header is syntactially valid, append to it
"~^(,[ \t]*)*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\t \x21\x23-\x5B\x5D-\x7E\x80-\xFF]|\\[\t \x21-\x7E\x80-\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\t \x21\x23-\x5B\x5D-\x7E\x80-\xFF]|\\[\t \x21-\x7E\x80-\xFF])*\"))?)*([ \t]*,([ \t]*([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\t \x21\x23-\x5B\x5D-\x7E\x80-\xFF]|\\[\t \x21-\x7E\x80-\xFF])*\"))?(;([!#$%&'*+.^_`|~0-9A-Za-z-]+=([!#$%&'*+.^_`|~0-9A-Za-z-]+|\"([\t \x21\x23-\x5B\x5D-\x7E\x80-\xFF]|\\[\t \x21-\x7E\x80-\xFF])*\"))?)*)?)*$" "$http_forwarded, $proxy_forwarded_elem";
# Otherwise, replace it
default "$proxy_forwarded_elem";
}
# Load configs
include /etc/nginx/conf.d/localhost.conf;
}
和localhost.conf
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name localhost;
root /usr/share/nginx/html;
ssl_certificate /etc/nginx/live/localhost/cert.pem;
ssl_certificate_key /etc/nginx/live/localhost/key.pem;
include /etc/nginx/conf.d/security.conf;
include /etc/nginx/conf.d/proxy.conf;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log info;
# nginx render files or proxy the request
location / {
try_files $uri @front;
}
location @front {
proxy_pass http://frontend:80;
}
location ^~ /api/v1 {
proxy_read_timeout 30m; # because an inference with SIMP can takes some time
proxy_send_timeout 30m;
proxy_connect_timeout 30m;
proxy_pass http://backend:4000;
}
location = /report.html {
root /usr/share/goaccess/html/;
}
location ^~ /ws {
proxy_pass http://goaccess:7890;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_read_timeout 7d;
proxy_connect_timeout 3600;
}
include /etc/nginx/conf.d/general.conf;
}
编辑:
请求是通过 Angular HttpClient
发送的,也许这个模块是 built 的一种方式,如果在短时间内没有发送响应,我会尝试中止请求对此进行调查。
好的,我想我可以回答我自己的问题。 HTTP 请求不是为长请求设计的。发出请求后,应尽快提供响应。
当你在做一个长时间的工作时,你应该使用 worker 和消息架构(或事件驱动架构)以及像 rabbitmq 或 kafka 这样的工具。您也可以使用轮询(但这不是更有效的解决方案)。
因此,在我的 POST 处理程序中,我应该做的是在数据到达时向我的代理发送一条消息,然后发出适当的响应(比如正在处理请求)。 工作人员订阅一个队列,可以接收之前发送的消息,完成工作,然后回复我的后端。然后我们可以使用 STOMP (websocket) 插件将结果路由到前端。