502 Bad Gateway 意外 EOF

您正在查看的是 Apigee Edge 文档。
转到 Apigee X 文档
信息

问题

客户端应用收到 HTTP 状态代码 502 以及消息 Bad Gateway,作为 API 调用的响应。

HTTP 状态代码 502 表示客户端未收到实际应完成请求的有效响应。

错误消息

客户端应用会获得以下响应代码:

HTTP/1.1 502 Bad Gateway

此外,您可能还会看到以下错误消息:

{
   "fault": {
      "faultstring": "Unexpected EOF at target",
      "detail": {
           "errorcode": "messaging.adaptors.http.UnexpectedEOFAtTarget"
       }
    }
}

可能的原因

Unexpected EOF 错误是导致 502 Bad Gateway Error 的一个典型原因,该错误可能由以下原因导致:

原因 具体说明 针对以下项目提供的步数:
目标服务器配置不正确 目标服务器未正确配置,无法支持 TLS/SSL 连接。 Edge 公有云和私有云用户
来自后端服务器的 EOFException 后端服务器可能会突然发送 EOF。 仅限 Edge Private Cloud 用户
keep alive 超时配置错误 Apigee 和后端服务器上的保持活跃超时配置不正确。 Edge 公有云和私有云用户

常见诊断步骤

您可以使用以下任一方法诊断错误:

API 监控

要使用 API Monitoring 诊断错误,请执行以下操作:

您可以按照调查问题中所述的步骤,使用 API Monitoring 调查 502 错误。也就是说:

  1. 前往调查信息中心。
  2. 从下拉菜单中选择状态代码 ,并确保在发生 502 错误时选择了正确的时间段。
  3. 如果您看到大量 502 错误,请点击矩阵中的方框。
  4. 在右侧,点击与如下所示的 502 错误对应的查看日志
  5. 在这里,我们可以看到以下信息:

    • Fault Sourcetarget
    • Fault Codemessaging.adaptors.http.UnexpectedEOFAtTarget

这表示 502 错误是由目标因意外的 EOF 导致的。

此外,请记下 502 错误的 Request Message ID,以进行进一步调查。

跟踪工具

如需使用跟踪工具诊断错误,请执行以下操作:

  1. 启用 跟踪会话,并进行 API 调用以重现问题 502 Bad Gateway
  2. 选择其中一个失败的请求并检查跟踪记录。
  3. 浏览跟踪记录的各个阶段,并找到失败的位置。
  4. 在请求发送到目标服务器后,您应该会看到失败,如下所示:

    alt_text

    alt_text

  5. 确定跟踪记录中 AX(Analytics 数据记录)阶段中的 X-Apigee.fault-sourceX-Apigee.fault-code 的值。

    如果 X-Apigee.fault-sourceX-Apigee.fault-code 的值与下表中显示的值匹配,您可以确认 502 错误来自目标服务器:

    响应标头
    X-Apigee.fault-source target
    X-Apigee.fault-code messaging.adaptors.http.flow.UnexpectedEOFAtTarget

    此外,请记下 502 错误的 X-Apigee.Message-ID,以进行进一步调查。

NGINX 访问日志

如需使用 NGINX 诊断错误,请执行以下操作:

您还可以参考 NGINX 访问日志来确定导致 502 状态代码的原因。如果问题是过去发生的,或者问题是间歇性的,并且您无法在界面中捕获跟踪记录,这种方法特别有用。如需从 NGINX 访问日志中确定此信息,请按以下步骤操作:

  1. 查看 NGINX 访问日志。
    /opt/apigee/var/log/edge-router/nginx/ORG~ENV.PORT#_access_log
  2. 搜索特定 API 代理在特定持续时间(如果问题是过去发生的)内出现的任何 502 错误,或搜索仍然失败且显示 502 的任何请求。
  3. 如果存在任何 502 错误,则检查错误是否是由目标发送 Unexpected EOF 引起的。如果 X-Apigee.fault-sourceX- Apigee.fault-code 的值与下表中显示的值匹配,则 502 错误是由目标意外关闭连接引起的:
    响应标头
    X-Apigee.fault-source target
    X-Apigee.fault-code messaging.adaptors.http.flow.UnexpectedEOFAtTarget

    以下示例条目显示了目标服务器导致的 502 错误:

此外,请记下 502 错误的邮件 ID,以便展开进一步调查。

原因:目标服务器配置不正确

目标服务器未正确配置,无法支持 TLS/SSL 连接。

诊断

  1. 使用 API 监控跟踪工具NGINX 访问日志来确定 502 错误的消息 ID、错误代码和故障来源。
  2. 在界面中为受影响的 API 启用跟踪记录。
  3. 如果失败的 API 请求的跟踪记录显示以下内容:
    1. 目标流请求一旦启动,便会看到 502 Bad Gateway 错误。
    2. error.class 会显示 messaging.adaptors.http.UnexpectedEOF.

      则此问题很可能是由错误的目标服务器配置导致的。

  4. 使用 Edge Management API 调用获取目标服务器定义:
    1. 如果您是公有云用户,请使用以下 API:
      curl -v https://api.enterprise.apigee.com/v1/organizations/<orgname>/environments/<envname>/targetservers/<targetservername> -u <username>
      
    2. 如果您是 Private Cloud 用户,请使用以下 API:
      curl -v http://<management-server-host>:<port #>/v1/organizations/<orgname>/environments/<envname>/targetservers/<targetservername> -u <username>
      

      错误的 TargetServer 定义示例:

      <TargetServer  name="target1">
        <Host>mocktarget.apigee.net</Host>
        <Port>443</Port>
        <IsEnabled>true</IsEnabled>
      </TargetServer >
      
  5. 图中的 TargetServer 定义是其中一项典型错误配置的示例,相关说明如下:

    假设目标服务器 mocktarget.apigee.net 已配置为在端口 443 上接受安全 (HTTPS) 连接。但是,在查看目标服务器定义时,您会发现其中没有任何其他属性/标志表明它适用于安全连接。这会导致 Edge 将发往特定目标服务器的 API 请求视为 HTTP(非安全)请求。因此 Edge 不会与此目标服务器启动 SSL 握手流程。

    由于目标服务器配置为仅接受 443 上的 HTTPS (SSL) 请求,因此它会拒绝来自 Edge 的请求或关闭连接。因此,您会在消息处理器上遇到 UnexpectedEOFAtTarget 错误。消息处理器将发送 502 Bad Gateway 作为对客户端的响应。

分辨率

始终确保目标服务器配置符合您的要求。

对于上述示例,如果您要向安全 (HTTPS/SSL) 目标服务器发出请求,则需要添加 SSLInfo 属性,并将 enabled 标志设置为 true。虽然可以在目标端点定义本身中添加目标服务器的 SSLInfo 属性,但建议在目标服务器定义中添加 SSLInfo 属性,以避免混淆。

  1. 如果后端服务需要单向 SSL 通信,则:
    1. 您需要添加 SSLInfo 属性,并将 enabled 标志设置为 true,从而在 TargetServer 定义中启用 TLS/SSL,如下所示:
      <TargetServer name="mocktarget">
        <Host>mocktarget.apigee.net</Host>
        <Port>443</Port>
        <IsEnabled>true</IsEnabled>
        <SSLInfo>
            <Enabled>true</Enabled>
        </SSLInfo>
      </TargetServer>
      
    2. 如果要在 Edge 中验证目标服务器的证书,我们还需要添加信任库(包含目标服务器的证书),如下所示:
      <TargetServer  name="mocktarget">
          <Host>mocktarget.apigee.net</Host>
          <Port>443</Port>
          <IsEnabled>true</IsEnabled>
          <SSLInfo>
              <Ciphers/>
              <ClientAuthEnabled>false</ClientAuthEnabled>
              <Enabled>true</Enabled>
              <IgnoreValidationErrors>false</IgnoreValidationErrors>
              <Protocols/>
              <TrustStore>mocktarget-truststore</TrustStore>
          </SSLInfo>
      </TargetServer>
      
  2. 如果后端服务需要双向 SSL 通信,则:
    1. 您需要为 SSLInfo 属性设置适当的 ClientAuthEnabledKeystoreKeyAliasTruststore 标志,如下所示:
      <TargetServer  name="mocktarget">
           <IsEnabled>true</IsEnabled>
           <Host>www.example.com</Host>
           <Port>443</Port>
           <SSLInfo>
               <Ciphers/>
               <ClientAuthEnabled>true</ClientAuthEnabled>
               <Enabled>true</Enabled>
               <IgnoreValidationErrors>false</IgnoreValidationErrors>
               <KeyAlias>keystore-alias</KeyAlias>
               <KeyStore>keystore-name</KeyStore>
               <Protocols/>
               <TrustStore>truststore-name</TrustStore>
           </SSLInfo>
        </TargetServer >
      

参考

跨后端服务器进行负载均衡

原因:来自后端服务器出现 EOFException

后端服务器可能会突然发送 EOF(文件结束)。

诊断

  1. 使用 API 监控跟踪工具NGINX 访问日志来确定 502 错误的消息 ID、错误代码和故障来源。
  2. 检查消息处理器日志 (/opt/apigee/var/log/edge-message-processor/logs/system.log) 并搜索,查看特定 API 是否有 eof unexpected,或者 API 请求是否有唯一的 messageid,然后进行搜索。

    消息处理器日志中的异常堆栈轨迹示例

    "message": "org:myorg env:test api:api-v1 rev:10 messageid:rrt-1-14707-63403485-19 NIOThread@0 ERROR HTTP.CLIENT - HTTPClient$Context$3.onException() : SSLClientChannel[C:193.35.250.192:8443 Remote host:0.0.0.0:50100]@459069 useCount=6 bytesRead=0 bytesWritten=755 age=40107ms lastIO=12832ms .onExceptionRead exception: {}
    java.io.EOFException: eof unexpected
    at com.apigee.nio.channels.PatternInputChannel.doRead(PatternInputChannel.java:45) ~[nio-1.0.0.jar:na]
    at com.apigee.nio.channels.InputChannel.read(InputChannel.java:103) ~[nio-1.0.0.jar:na]
    at com.apigee.protocol.http.io.MessageReader.onRead(MessageReader.java:79) ~[http-1.0.0.jar:na]
    at com.apigee.nio.channels.DefaultNIOSupport$DefaultIOChannelHandler.onIO(NIOSupport.java:51) [nio-1.0.0.jar:na]
    at com.apigee.nio.handlers.NIOThread.run(NIOThread.java:123) [nio-1.0.0.jar:na]"
    

    在上面的示例中,您可以看到消息处理器尝试从后端服务器读取响应时发生 java.io.EOFException: eof unexpected 错误。此异常表示文件结束 (EOF),或流的结尾意外到达。

    也就是说,消息处理器将 API 请求发送到后端服务器,并正在等待或读取响应。但是,在消息处理器收到响应或读取完整响应之前,后端服务器突然终止了连接。

  3. 检查后端服务器日志,看看是否有任何错误或信息可能导致后端服务器突然终止连接。如果您发现任何错误/信息,请转到解决,并在后端服务器中相应解决问题。
  4. 如果您在后端服务器中找不到任何错误或信息,请收集消息处理器上的 tcpdump 输出:
    1. 如果您的后端服务器主机只有一个 IP 地址,请使用以下命令:
      tcpdump -i any -s 0 host IP_ADDRESS -w FILE_NAME
      
    2. 如果您的后端服务器主机有多个 IP 地址,请使用以下命令:
      tcpdump -i any -s 0 host HOSTNAME -w FILE_NAME
      

      通常,此错误是因为消息处理器将请求发送到后端服务器后,后端服务器立即以 [FIN,ACK] 进行响应。

  5. 请参考以下 tcpdump 示例。

    502 Bad Gateway Error (UnexpectedEOFAtTarget) 发生时获取的示例 tcpdump

  6. TCPDump 输出中,您会看到以下事件序列:
    1. 在数据包 985 中,消息处理器将 API 请求发送到后端服务器。
    2. 在数据包 986 中,后端服务器立即返回 [FIN,ACK]
    3. 在数据包 987 中,消息处理器向后端服务器做出 [FIN,ACK] 响应。
    4. 最终,通过两端的 [ACK][RST] 关闭连接。
    5. 由于后端服务器发送 [FIN,ACK],因此消息处理器上会出现 java.io.EOFException: eof unexpected 异常。
  7. 如果后端服务器出现网络问题,就可能会发生这种情况。请与您的网络运营团队合作,进一步调查此问题。

分辨率

妥善修复后端服务器上的问题。

如果问题仍然存在,并且您需要有关排查 502 Bad Gateway Error 问题的帮助,或您怀疑这是 Edge 中存在的问题,请与 Apigee Edge 支持团队联系。

原因:保留活动超时配置不正确

在诊断这是否是导致 502 错误的原因之前,请仔细阅读以下概念。

Apigee 中的持久连接

默认情况下,Apigee(以及后续的 HTTP/1.1 标准)在与目标后端服务器通信时使用持久连接。永久性连接允许重复使用已建立的 TCP 和(如果适用)TLS/SSL 连接,从而提高性能,减少延迟开销。需要保留连接的时长通过 keep alive timeout 属性 (keepalive.timeout.millis) 控制。

后端服务器和 Apigee 消息处理器都使用 keep-alive 超时来保持相互连接。一旦未在 keep-alive 超时期限内收到任何数据,后端服务器或消息处理器即可关闭与对方的连接。

默认情况下,部署到 Apigee 中的消息处理器的 API 代理会将 keep alive 超时设置为 60s,除非被替换。未收到 60s 的数据后,Apigee 将关闭与后端服务器的连接。后端服务器还会保持 keep-alive 超时,一旦该超时,后端服务器将关闭与消息处理器的连接。

保持活动超时配置不正确的影响

如果 Apigee 或后端服务器配置了不正确的 keep-alive 超时,则会导致竞态条件,导致后端服务器发送意外的 End Of File (FIN) 来响应资源请求。

例如,如果在 API 代理或消息处理器中配置了 keep-alive 超时,且值大于或等于上游后端服务器的超时值,则可能会出现以下竞态条件。也就是说,如果消息处理器在非常接近后端服务器保持活动超时的阈值之前未收到任何数据,则请求将通过现有连接发送到后端服务器。这可能会导致 502 Bad Gateway,因为发生意外的 EOF 错误,如下所述:

  1. 假设消息处理器和后端服务器上的 keep-alive 超时均为 60 秒,并且直到特定消息处理器处理上一个请求后 59 秒内才会收到新请求。
  2. 消息处理器会继续操作,使用现有连接(因为 keep-alive 超时尚未过去)处理在第 59 秒收到的请求,然后将该请求发送到后端服务器。
  3. 但是,在请求到达后端服务器之前,后端服务器上已超出了 keepalive 超时阈值。
  4. 消息处理器正在请求资源,但后端服务器会尝试向消息处理器发送 FIN 数据包来关闭连接。
  5. 在消息处理器等待接收数据时,它会改为收到意外的 FIN,并终止连接。
  6. 这会导致 Unexpected EOF,随后消息处理器将 502 返回给客户端。

在本例中,我们观察到发生了 502 错误,因为消息处理器和后端服务器上都配置了相同的 keep-alive 超时值(60 秒)。同样,如果为消息处理器上配置的保持活动超时值高于后端服务器上的值,也可能会出现此问题。

诊断

  1. 如果您是公有云用户
    1. 使用 API 监控或跟踪工具(如常见诊断步骤中所述),确认您已完成以下两项设置:
      • 错误代码messaging.adaptors.http.flow.UnexpectedEOFAtTarget
      • 故障来源target
    2. 请参阅使用 tcpdump 进行进一步调查。
  2. 如果您是 Private Cloud 用户
    1. 使用跟踪工具NGINX 访问日志确定 502 错误的消息 ID、故障代码和故障来源。
    2. 在消息处理器日志
      (/opt/apigee/var/log/edge-message-processor/logs/system.log) 中搜索消息 ID。
    3. 您将看到如下所示的 java.io.EOFEXception: eof unexpected
      2020-11-22 14:42:39,917 org:myorg env:prod api:myproxy rev:1 messageid:myorg-opdk-dc1-node2-17812-56001-1  NIOThread@1 ERROR HTTP.CLIENT - HTTPClient$Context$3.onException() :  ClientChannel[Connected: Remote:51.254.225.9:80 Local:10.154.0.61:35326]@12972 useCount=7 bytesRead=0 bytesWritten=159 age=7872ms  lastIO=479ms  isOpen=true.onExceptionRead exception: {}
              java.io.EOFException: eof unexpected
              at com.apigee.nio.channels.PatternInputChannel.doRead(PatternInputChannel.java:45)
              at com.apigee.nio.channels.InputChannel.read(InputChannel.java:103)
              at com.apigee.protocol.http.io.MessageReader.onRead(MessageReader.java:80)
              at com.apigee.nio.channels.DefaultNIOSupport$DefaultIOChannelHandler.onIO(NIOSupport.java:51)
              at com.apigee.nio.handlers.NIOThread.run(NIOThread.java:220)
      
    4. 错误 java.io.EOFException: eof unexpected 表示消息处理器在等待读取来自后端服务器的响应时收到了 EOF
    5. 上述错误消息中的 useCount=7 属性表示消息处理器已重复使用此连接约七次,bytesWritten=159 属性表示消息处理器已将 159 字节的请求载荷发送到后端服务器。不过,当意外发生 EOF 时,它收到了 0 字节的返回。
    6. 这表明消息处理器多次重复使用同一连接,此时它发送了数据,但不久之后就收到了 EOF,而没有收到任何数据。这意味着后端服务器的 keep-alive 超时很有可能短于或等于 API 代理中设置的超时时间。

      您可以在 tcpdump 的帮助下进行进一步调查,如下所述。

使用 tcpdump

  1. 使用以下命令在后端服务器上捕获 tcpdump
    tcpdump -i any -s 0 host MP_IP_Address -w File_Name
    
  2. 分析捕获的 tcpdump

    以下是 tcpdump 输出示例

    在上面的示例 tcpdump 中,您可以看到以下内容:

    1. 在数据包 5992, 中,后端服务器收到了 GET 请求。
    2. 在数据包 6064 中,它会做出 200 OK. 响应
    3. 在数据包 6084 中,后端服务器收到了另一个 GET 请求。
    4. 在数据包 6154 中,它会做出 200 OK 响应。
    5. 在数据包 6228 中,后端服务器收到了第三个 GET 请求。
    6. 这一次,后端服务器会向消息处理器(数据包 6285)返回一个 FIN, ACK,以启动连接关闭。

    在此示例中,相同的连接成功重复使用了两次,但在收到第三个请求时,后端服务器发起连接关闭,同时消息处理器正在等待来自后端服务器的数据。这表明后端服务器的保持活动超时时间很可能更短或等于 API 代理中设置的值。如需验证这一点,请参阅比较 Apigee 和后端服务器上的 keepalive 超时

比较 Apigee 和后端服务器上的 keepalive 超时情况

  1. 默认情况下,Apigee 为 keep-alive 超时属性使用 60 秒的值。
  2. 不过,您可能替换了 API 代理中的默认值。 您可以通过检查给出 502 错误的失败 API 代理中的特定 TargetEndpoint 定义来验证这一点。

    TargetEndpoint 配置示例

    <TargetEndpoint name="default">
      <HTTPTargetConnection>
        <URL>https://mocktarget.apigee.net/json</URL>
        <Properties>
          <Property name="keepalive.timeout.millis">30000</Property>
        </Properties>
      </HTTPTargetConnection>
    </TargetEndpoint>
    

    在上面的示例中, keep alive 超时属性被替换为 30 秒(30000 毫秒)值。

  3. 接下来,检查后端服务器上配置的 keep alive 超时属性假设您的后端服务器配置为使用 25 seconds 值。
  4. 如果您确定 Apigee 上的 keepalive 超时属性的值大于后端服务器上的 keep-alive 超时属性的值,如上例所示,这就是导致 502 错误的原因。

分辨率

确保 Apigee(在 API 代理和消息处理器组件中)的保持活跃超时属性始终低于后端服务器上的活跃超时属性。

  1. 确定为后端服务器上的 keep alive 超时设置的值。
  2. 按照 在消息处理器上配置 keepalive 超时中所述的步骤,在 API 代理或消息处理器中为 keep-alive 超时属性配置适当的值,使 keep-alive 超时属性低于在后端服务器上设置的值。

如果问题仍然存在,请转到必须收集诊断信息

最佳做法

强烈建议下游组件具有的保持活动超时阈值始终小于上游服务器上配置的阈值,以避免此类竞态条件和 502 错误。每个下行跃点应低于每个上行跃点。在 Apigee Edge 中,最好遵循以下准则:

  1. 客户端 keep alive 超时应小于边缘路由器 keep alive 超时。
  2. 边缘路由器 keep alive 超时应小于消息处理器 keep alive 超时。
  3. 消息处理器 keep alive 超时应小于目标服务器 keep alive 超时。
  4. 如果 Apigee 之前或之后有任何其他跃点,则应应用相同的规则。您应该始终让下游客户端负责关闭与上游的连接。

必须收集诊断信息

如果按照上述说明操作后,问题仍然存在,请收集以下诊断信息,然后联系 Apigee Edge 支持团队

如果您是公有云用户,请提供以下信息:

  • 组织名称
  • 环境名称
  • API 代理名称
  • 完成 curl 命令以重现 502 错误
  • 包含出现 502 Bad Gateway - Unexpected EOF 错误的请求的跟踪文件
  • 如果当前未发生 502 错误,请提供过去发生 502 错误时的时区信息对应的时间段。

如果您是 Private Cloud 用户,请提供以下信息:

  • 观察到失败请求的完整错误消息
  • 您观察到 502 错误的组织、环境名称和 API 代理名称
  • API 代理软件包
  • 包含出现 502 Bad Gateway - Unexpected EOF 错误的请求的跟踪文件
  • NGINX 访问日志
    /opt/apigee/var/log/edge-router/nginx/ORG~ENV.PORT#_access_log
  • 消息处理器日志
    /opt/apigee/var/log/edge-message-processor/logs/system.log
  • 发生 502 错误时包含时区信息的时间段
  • 发生错误时,在消息处理器和/或后端服务器上收集的 Tcpdumps