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"
       }
    }
}

可能的原因

502 Bad Gateway Error 的常见原因之一是 Unexpected EOF 错误,该错误可能是由以下原因造成的:

原因 详细信息 针对以下情况提供的步骤
目标服务器配置不正确 目标服务器未正确配置为支持 TLS/SSL 连接。 Edge Public 和 Private Cloud 用户
来自后端服务器的 EOFException 后端服务器可能会突然发送 EOF。 仅限 Edge Private Cloud 用户
keep-alive 超时配置不正确 Apigee 和后端服务器上的 keep-alive 超时配置不正确。 Edge Public 和 Private Cloud 用户

常见诊断步骤

如需诊断此错误,您可以使用以下任一方法:

API 监控

如需使用 API 监控功能诊断错误,请执行以下操作:

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

  1. 前往调查信息中心。
  2. 在下拉菜单中选择状态代码 ,并确保在发生 502 错误时选择了正确的时间段。
  3. 当您看到大量 502 错误时,请点击矩阵中的相应方框。
  4. 在右侧,点击 502 错误的查看日志,该错误看起来类似于以下内容:
  5. 在此处,我们可以看到以下信息:

    • 故障来源target
    • 故障代码messaging.adaptors.http.UnexpectedEOFAtTarget

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

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

Trace 工具

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

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

    alt_text

    alt_text

  5. 在跟踪的 AX(记录的分析数据)阶段中,确定 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 错误的 message ID,以便进一步调查。

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

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

诊断

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

      那么,此问题很可能是由目标服务器配置不正确造成的。

  4. 使用 Edge 管理 API 调用获取目标服务器定义:
    1. 如果您是公共云用户,请使用以下 API:
      curl -v https://api.enterprise.apigee.com/v1/organizations/<orgname>/environments/<envname>/targetservers/<targetservername> -u <username>
    2. 如果您是私有云用户,请使用以下 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. 您需要在 TargetServer 定义中启用 TLS/SSL,方法是添加 SSLInfo 属性,并将 enabled 标志设置为 true,如下所示:
      <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. 您需要设置具有 ClientAuthEnabledKeystoreKeyAliasTruststore 标志的 SSLInfo 属性,如下所示:
      <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 错误的 message 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 支持团队联系。

原因:keep-alive 超时配置不正确

在诊断 502 错误是否由此原因导致之前,请先阅读以下概念。

Apigee 中的持久性连接

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

后端服务器和 Apigee 消息处理器都使用 keep-alive 超时来保持彼此之间的连接处于打开状态。如果在保持活动状态超时时长内未收到任何数据,后端服务器或消息处理器可以关闭与另一方的连接。

默认情况下,部署到 Apigee 中的消息处理器的 API 代理的 keep-alive 超时时间设置为 60s,除非被覆盖。如果在 60s 内未收到任何数据,Apigee 将关闭与后端服务器的连接。后端服务器也会维护一个 keepalive 超时,一旦超时,后端服务器就会关闭与消息处理器的连接。

keep-alive 超时配置不正确的含义

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

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

  1. 假设在消息处理器和后端服务器上设置的 keep-alive 超时时间均为 60 秒,并且在特定消息处理器处理完上一个请求后的 59 秒内没有新请求。
  2. 消息处理器继续使用现有连接(因为保持活动状态超时时间尚未到期)处理在第 59 秒收到的请求,并将该请求发送到后端服务器。
  3. 不过,在请求到达后端服务器之前,后端服务器上的 Keep-Alive 超时阈值已被超出。
  4. 消息处理器对资源的请求正在传输中,但后端服务器尝试通过向消息处理器发送 FIN 数据包来关闭连接。
  5. 当消息处理器等待接收数据时,它却收到了意外的 FIN,连接随即终止。
  6. 这会导致消息处理器向客户端返回 Unexpected EOF,随后返回 502

在这种情况下,我们发现之所以出现 502 错误,是因为消息处理器和后端服务器上都配置了相同的 keep-alive 超时值(即 60 秒)。同样,如果消息处理器的 keep-alive 超时时间配置的值高于后端服务器上的值,也可能会出现此问题。

诊断

  1. 如果您是公有云用户,请执行以下操作:
    1. 使用 API 监控或 Trace 工具(如常见诊断步骤中所述),并验证您是否同时具有以下两项设置:
      • 故障代码messaging.adaptors.http.flow.UnexpectedEOFAtTarget
      • 故障来源target
    2. 如需进一步调查,请参阅使用 tcpdump
  2. 如果您是 Private Cloud 用户
    1. 使用跟踪工具NGINX 访问日志来确定 502 错误的 message 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 表示消息处理器已重复使用此连接约 7 次,而属性 bytesWritten=159 表示消息处理器已向后端服务器发送了 159 字节的请求载荷。但是,当发生意外的 EOF 时,它收到了零字节。
    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. 这次,后端服务器会向消息处理器返回一个 FIN, ACK(数据包 6285),以启动连接关闭。

    在此示例中,同一连接已成功重复使用两次,但在第三个请求中,后端服务器在消息处理器等待后端服务器的数据时启动了连接关闭。这表明后端服务器的 keep-alive 超时时间很可能小于或等于 API 代理中设置的值。如需验证这一点,请参阅比较 Apigee 和后端服务器上的 keep-alive 超时

比较 Apigee 和后端服务器上的 keep-alive 超时

  1. 默认情况下,Apigee 使用 60 秒作为 keep alive 超时属性的值。
  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 上的 keep-alive 超时属性的值高于后端服务器上的 keep-alive 超时属性的值(如上例所示),则会导致 502 错误。

分辨率

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

  1. 确定后端服务器上为 keep-alive 超时设置的值。
  2. 在 API 代理或消息处理器中为 keep-alive 超时属性配置适当的值,使该属性的值低于后端服务器上设置的值,具体步骤请参阅 在消息处理器上配置 keep-alive 超时

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

最佳做法

强烈建议下游组件的 keep-alive 超时阈值始终低于上游服务器上配置的阈值,以避免此类竞态条件和 502 错误。每个下游跃点的延迟都应低于每个上游跃点的延迟。在 Apigee Edge 中,建议遵循以下准则:

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

必须收集的诊断信息

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

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

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

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

  • 失败请求的完整错误消息
  • 您正在观察 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 在消息处理器或后端服务器上收集的,或者在发生错误时同时在两者上收集的