使用 Spring 云网关和 Nginx 作为反向代理的网关超时

Posted

技术标签:

【中文标题】使用 Spring 云网关和 Nginx 作为反向代理的网关超时【英文标题】:Gateway timeout with Spring cloud gateway and Nginx as reverse proxy 【发布时间】:2019-08-22 07:53:48 【问题描述】:

我为我的应用程序创建了一个 API 网关,它将充当其他微服务的前端控制器。 在我的生产设置中,我使用 nginx 作为网关的反向代理

API 网关在 8080 端口上运行

Nginx 配置如下:

网关-api.conf:

server 
    listen 80;
    server_name api.example.com;
    location / 
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass http://localhost:30010/;
        keepalive_timeout 500s;
    
    keepalive_timeout 500s;
    access_log /var/log/nginx/api.log;  
    error_log /var/log/nginx/api_error.log;

nginx.conf 中的超时设置:

proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
send_timeout 300;

Spring 云网关 gradle 文件:

compile('org.springframework.cloud:spring-cloud-starter-gateway')
 compile('org.springframework.cloud:spring-cloud-starter-openfeign')
 compile("org.springframework.boot:spring-boot-starter-actuator")
 compile('org.springframework.boot:spring-boot-starter-security')

springBootVersion=2.0.3.RELEASE
springDMPVersion=1.0.4.RELEASE
springPlatformBomVersion=Cairo-SR2
springCloudVersion=Finchley.RELEASE

网关应用:

@SpringBootApplication
@ComponentScan(basePackages = "com.example")
@EntityScan(basePackages = "com.example")
@EnableFeignClients(basePackages = "com.example")
public class GatewayApplication 

    public static void main(String[] args) 
        SpringApplication.run(GatewayApplication.class, args);
    

问题陈述:

在我的一个微服务中,一个 REST API 需要 3 多分钟才能完成。 如果我通过nginx(api.example.com) 调用此 API,它会在恰好在 1 分钟后失败并给出 HTTP 状态 504。

卷曲:

curl --request GET \
  --url http://api.example.com/hellomicroservice/api/take/moretime

错误:

504 Timeout while reading the response from Server

nginx 或 API 网关中没有错误日志。

从 nginx 访问日志:

203.129.213.102 - - [01/Apr/2019:08:14:33 +0000] "GET hellomicroservice/api/take/moretime HTTP/1.1" 499 0 "-" "PostmanRuntime/7.3.0"

但是当我直接调用同一个 API 到网关(在网关端口 8080 上)时,请求被成功处理。

curl 与网关端口:

curl --request GET \
  --url http://api.example.com:8080/hellomicroservice/api/take/moretime

编辑:

如果我将 Nginx 超时设置应用为小于 60 秒(例如 30 秒),则请求会在指定的时间间隔内超时。但是如果我将 Nginx 超时设置为超过 60 秒,我们设置为 300 秒,请求在 60 秒后超时。

【问题讨论】:

当你curl通过反向代理时,API网关上的错误日志和nginx错误日志是什么? nginx和网关没有错误日志,添加访问日志 可以试试加proxy_read_timeout 300s;在服务器块服务器 proxy_read_timeout 300s; @RadhaMohanMaheshwari 试过了,没用 你可以尝试用 api.example.com:8080 替换 nginx 设置中的 localhost:8080 吗? 【参考方案1】:

我想这是由于许多其他事情而可能发生的问题之一。这是一个对我有用的解决方案(我在/var/log/nginx/error.log 中也遇到了错误:

2020/12/30 21:47:47 [错误] 26765#26765: *13064 上游超时 (110:连接超时)同时连接到上游,客户端:XXX, 服务器:example.com,请求:“GET /eshop HTTP/1.0”,上游: “http://[::1]:8080/error_50x.html”,主机:“example.com”

奇怪的是,这并没有发生在我的笔记本电脑上,而只是发生在我的服务器上。所以我检查了 IP,结果可能是因为缺少 ::1 地址。当我将它添加到 lo 网络设备时,我无法复制超时。

sudo ip a add ::1/128 dev lo

下一步:(这是我的理解,我不是 100% 确定这一点:)此外,由于保持与 localhost Java 服务的连接的开销似乎高于仅在另一个请求时断开该连接并再次连接制作完成后,建议对代理使用以下设置(在您的 nginx 站点 .conf 中):

proxy_http_version 1.1;
proxy_set_header Connection "";

见https://***.com/a/10396874/3223505

【讨论】:

【参考方案2】:

尝试将您的超时设置放在 /etc/nginx/conf.d/timeout.conf 中(如果没有,请创建一个)。设置如下设置,

proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;

【讨论】:

【参考方案3】:

由于您的配置缺少 proxy_http_version 键,上游可能仍未启用 Keepalive。

引用自:https://www.nginx.com/blog/tuning-nginx/#proxy_http_version

要启用与上游服务器的保持连接,您还必须在配置中包含以下指令:

proxy_http_version 1.1;
proxy_set_header Connection "";

我也会按照 Kris 的建议将 keepalive_timeout 保留在配置中。

【讨论】:

您可能遇到了格式问题。示例配置显示“server”块通常包装在“http ”块中,“keepalive_timeout”键通常是 http 块的子项,而不是 server 或 location 块。您可以检查您的其他密钥以确保它们也都在正确的父项下。另外,配置目录中是否有多个 nginx 配置处于活动状态?【参考方案4】:

看来请求超时对您来说不是问题。它的连接超时。 我认为我们需要查看

的标题

连接

AFAIK,Connection 标头定义,连接应该是持久的,或者谁有权维护/关闭它。如果连接是 keep-alive ,那么连接将是持久的。对于保持连接,客户端偶尔会发送一个 TCP ping 以确保服务器仍然处于活动状态并保持连接。根据 curl,此时间默认为每 60 秒一次。

现在nginx 必须配置为接受连接并使用 keepalive_timeout 指令使其保持活动状态一段时间。如果不存在,那么nginx 将不会是keep the connections alive。

这应该是nginx在日志中说499的原因。 HTTP499 是 nginx 中的一个自定义错误,表示客户端关闭了连接。在您的情况下,curl 将其关闭。为什么curl关闭呢?因为 nginx 没有响应 60 秒的 TCP ping,因为没有启用 keep-alive。

keepalive_timeout 添加到 ~500 或比应用程序超时更高的值应该可以解决您的问题。

现在,为什么它直接与 tomcat 一起工作?我认为 spring 使活动超时可以是无限的或非常高的值。通常在tomcat中也是60秒。

我希望这能解决您的问题。

【讨论】:

设置keepalive_timeout 500;不起作用,同样的错误当我检查响应头时,我发现连接头为Connection →close 设置为keepalive_timeout 500s;将其保存在服务器部分或位置。我认为单位's'必须在那里。 在使用keepalive_timeout 500s; 放入服务器和位置部分后尝试但同样的错误 也许你应该尝试在 curl 中使用 --no-keepalive 选项。我假设 Nginx 不尊重保持连接。您还可以检查其他一些客户端,例如 http-client / Fiddler 或任何其他客户端。仅当您使用相同的连接执行多个操作时,才使用 keep-alive。 Connection 只是告诉源服务器在响应完成后如何处理 TCP 套接字,其想法是客户端将沿着流发送进一步的请求。在这种情况下,这将在 curl/postman 和 nginx 之间。我认为 nginx 更有可能只是感到无聊并终止了请求,这很奇怪,因为 read_timeout 是 5 分钟..

以上是关于使用 Spring 云网关和 Nginx 作为反向代理的网关超时的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud 网关和 NGINX

Nginx 反向代理导致 504 网关超时

微服务一文读懂网关概念+Nginx正反向代理+负载均衡+Spring Cloud Gateway(多栗子)

nginx是什么,如何使用

nginx简介

Nginx 代理管理器坏网关(Docker Wordpress 和 Nextcloud)