ホームページ >運用・保守 >Nginx >不適切な nginx 設定によって引き起こされる 499 およびフェイルオーバー メカニズムの障害の問題を解決する方法

不適切な nginx 設定によって引き起こされる 499 およびフェイルオーバー メカニズムの障害の問題を解決する方法

PHPz
PHPz転載
2023-06-02 19:54:241689ブラウズ

    #499

    499 の意味と考えられる理由は、実際には HTTP プロトコルの標準ステータス コードではなく、nginx のカスタム ステータス コードです。このステータス コードの明確な説明は、nginx の公式ドキュメントに記載されています。ブログ投稿からのより専門的な説明は次のとおりです:

    HTTP エラー 499 は、単にクライアントがエラー 499 であることを意味します。サーバーを介したリクエストの処理中にシャットダウンされます。エラー コード 499 は、クライアントで何かが発生したため、リクエストが実行できないことをより明確に示しています。心配しないでください。HTTP レスポンス コード 499 はあなたのせいではありません。

    一般的な考え方は、499 は一般に、HTTP リクエストの処理中にクライアントが処理プロセスを積極的に終了し、対応するネットワーク接続を切断することを意味します。499 は一般に、何らかの問題が発生していることを意味します。クライアント側で発生したものであり、サーバーとは関係ありません。
    以下は、nginx ソース コードの注釈です:

    /*
    * HTTP does not define the code for the case when a client closed
    * the connection while we are processing its request so we introduce
    * own code to log such situation when a client has closed the connection
    * before we even try to send the HTTP header to it
    */
    #define NGX_HTTP_CLIENT_CLOSED_REQUEST     499

    これは、クライアントが切断されたときに nginx がリクエストの処理を完了していないシナリオを記録するために、nginx がカスタム コード 499 を導入したことを意味します。
    何年も前に、私が初めて 499 のシーンに遭遇したとき、インターネットで情報を検索したときにも同様の回答を見たことがありました。そのため、499 はサーバーとはほとんど関係がなく、すべてが原因であるはずだと常に考えていました。クライアントによって。

    499 につながるクライアントの積極的な行動の例

    私はかつて検索 Lenovo インターフェイスに遭遇しましたが、その 499 の比率は他の API の数十倍でした -- Yi Qi Jue Chen 氏、この API は基本的に、長い間アラームしきい値を上回っていました。また、例外の具体的な理由も追跡しました。最終的に、クライアント パートナーと協力して結論に達しました。499 比率は正常です。

    • この API の呼び出しシナリオでは、ユーザーが検索ボックスに検索語を入力すると、ユーザーが文字を入力するたびに、 API は最新の入力で即座に呼び出され、返された関連付け結果がユーザーに表示されるため、Lenovo のほぼリアルタイムの検索機能が実現されます。

    • ユーザーが新しい文字を入力するたびに、最新の API 呼び出しリクエストがトリガーされるため、前の呼び出しリクエストがまだ進行中であっても、クライアントはこれらの呼び出しリクエストを直接終了する必要があります。 nginx ログに反映される古いリクエストは、クライアントがアクティブに切断したものです。

    したがって、Lenovo API の検索は、通常の API の 499 という高い比率とは異なりますが、これは完全に合理的です。クライアントは積極的に切断する責任がありますが、何もしていません。サーバー側には問題ありません。

    クライアントの受動的動作が 499 を引き起こす例

    クライアントの動作が 499 を引き起こすと以前考えられていたもう 1 つの例は、プッシュ ピークです。一部のユーザーは、プッシュでアプリを開いた後、即座にアプリを強制終了する場合があります。ピークプッシュ期間中は、通常、サーバーへの負荷が比較的高く、応答自体がオフピーク期間よりも遅くなります。この時点では、一部の API リクエストがまだ進行中である可能性があります。このとき、ユーザーはアプリを強制終了します - アプリは不当に終了し、無力です - そして、対応する接続​​は自然に切断され、OS によってリサイクルされ、結果的に 499 になります。 このシナリオでは、サーバー側に問題はありません。

    サーバーの問題が 499 を引き起こす可能性がありますか?

    上記の 2 つの例を通して、一見すると、499 は、能動的な動作であるか受動的な動作であるかにかかわらず、クライアント側によって引き起こされています。これら 2 つの例は、サーバー上で 499 を無視すべきであるというブロガーの考えを深めます。側面 責任の意識。
    サーバー側のエラーによって発生する可能性のある nginx エラー コードを要約すると、主なシナリオは次のようになります:

    • 500: 内部エラー。通常はリクエスト パラメーターが直接原因です。上流の処理スレッド コードの実行時にエラーが発生する ビジネス コードまたはフレームワークが直接内部エラーを返します

    • 502: 一般に、上流のサーバーは直接ハングし、接続できません。nginx はアクセスできません

    • 503: アップストリームの負荷が高すぎます -- しかし、ハングせず、直接 Service Unavailable
    • に戻りました。
    • 504: アップストリームの処理リクエストに時間がかかりすぎるため、アップストリームが戻るのを待っている間に nginx がタイムアウトします。ゲートウェイ タイムアウト
    • したがって、コード実行エラーであるかどうかにかかわらず、サービスはハングするか、サービスがビジーすぎるか、リクエストの処理に時間がかかりすぎて HTTP リクエストが失敗すると、5XX が返され、まったくトリガーされません。
    一般的に言えば、これは事実ですが、今回の新しい Pingfeng 499 は一般的な状況ではありません。インターネットで情報を検索すると、サーバーの処理に時間がかかりすぎることが nginx 499 の原因ではないかと示唆する人もいます。はい、ただし、上記の説明によれば、この状況はシナリオ 4 に属すべきではありません。アップストリームはリクエストの処理に時間がかかりすぎるため、nginx は 504 を返しますよね。

    サーバー側の処理に時間がかかりすぎるため、クライアントがアクティブに切断 499 したり、nginx がゲートウェイ タイムアウト 504 を返したりする可能性があるようです。では、この違いをもたらす主な要因は何でしょうか?
    簡単に言うと、クライアントが最初に切断し、nginx によって検出された場合は 499 になります。アップストリームに時間がかかりすぎて、タイムアウトが最初に nginx によって決定された場合は、504 になります。したがって、鍵となるのは nginx の時間ですアップストリーム タイムアウトの設定はここにあります。nginx のタイムアウト関連の設定を簡単に調べてみました。関連するタイムアウト期間は明示的に設定されていませんでした。

    504 判定に関連する nginx のタイムアウト設定

    API と nginx は uwsgi プロトコルを通じて通信するため、主要なタイムアウト設定パラメータは次のとおりです:

    Syntax:	uwsgi_connect_timeout time;
    Default:	
    uwsgi_connect_timeout 60s;
    Context:	http, server, location
    Defines a timeout for establishing a connection with a uwsgi server. It should be noted that this timeout cannot usually exceed 75 seconds.
    Syntax:	uwsgi_send_timeout time;
    Default:	
    uwsgi_send_timeout 60s;
    Context:	http, server, location
    Sets a timeout for transmitting a request to the uwsgi server. The timeout is set only between two successive write operations, not for the transmission of the whole request. If the uwsgi server does not receive anything within this time, the connection is closed.
    Syntax:	uwsgi_read_timeout time;
    Default:	
    uwsgi_read_timeout 60s;
    Context:	http, server, location
    Defines a timeout for reading a response from the uwsgi server. The timeout is set only between two successive read operations, not for the transmission of the whole response. If the uwsgi server does not transmit anything within this time, the connection is closed.

    在未明确指定的情况下其超时时间均默认为60s,简单来说(实际情况更复杂一些但这里不进一步探讨)只有在upstream处理请求耗时超过60s的情况下nginx才能判定其Gateway Timeout 并按照504处理,然而客户端设置的HTTP请求超时时间其实只有15s--这其中还包括外网数据传输的时间,于是问题来了:每一个服务端处理耗时超过15s的请求,nginx由于还没达到60s的超时阈值不会判定504,而客户端则会由于超过本地的15s超时时间直接断开连接,nginx于是就会记录为499。
    通过回查nginx log,非高峰期的499告警时段确实是存在单台upstream 请求处理缓慢,耗时过长,于是可能导致:

    • 用户在需要block等待请求的页面等待虽然不到15s但是已经不耐烦了,直接采取切页面或者杀死app重启的方式结束当前请求。

    • 用户耐心等待了15s、或者非阻塞的后台HTTP请求超过了15s超过超时阈值主动断开连接结束了当前请求。

    服务端耗时过长导致的499

    上面已经知道近期新出现的单台upstream 偶发499是由于响应缓慢引起的,既然是由于客户端超时时间(15s)远小于nginx upstream超时时间(60s)引起的,这应该属于一个明显的配置不当,会导致三个明显的问题:

    • 将用户由于各种原因(如杀app)很快主动断开连接导致的499与客户端达到超时时间(这里是15s)导致的499混在了一起,无法区分客户端责任与服务端责任导致499问题。

    • 对于nginx判定为499的请求,由于认为是客户端主动断开,不会被认为是服务端导致的unsuccessful attempt而被计入用于failover判定的max_fails计数中,所以即便一个upstream大量触发了499,nginx都不会将其从可用upstream中摘除,相当于摘除不可用节点的功能失效,而由于负载过高导致499的upstream收到的请求依然不断增加最终可能导致更大的问题。

    • 对于判定为499的请求,也是由于不会被认为是unsuccessful attempt,所以uwsgi_next_upstream这一配置也不会work,于是当第一个处理请求的upstream耗时过长超时后,nginx不会尝试将其请求转发为下一个upstream尝试处理后返回,只能直接失败。

    那是不是把客户端超时时间调大?或者把nginx upstream超时时间调小解决呢?
    调大客户端超时时间当然是不合理的,任何用户请求15s还未收到响应肯定是有问题的,所以正确的做法应该是调小upstream的超时时间,一般来说服务端对于客户端请求处理时间应该都是在数十、数百ms之间,超过1s就已经属于超长请求了,所以不但默认的60s不行,客户端设置的15s也不能用于upstream的超时判定。
    最终经过综合考虑服务端各api的耗时情况,先敲定了一个upstream 5s的超时时间配置--由于之前没有经验首次修改步子不迈太大,观察一段时间后继续调整,这样做已经足以很大程度解决以上的3个问题:

    • 将用户由于各种原因(如杀app)很快主动断开连接导致的499与nginx达到upstream超时时间时主动结束的504区分开了。

    • 504会被纳入max_fails计算,触发nginx摘除失败节点逻辑,在单台机器故障响应缓慢时可以被识别出来暂时摘除出可用节点列表,防止其负载进一步加大并保证后续请求均被正常可用节点处理返回。

    • 当nginx等待upstream处理达到5s触发超时时,其会按照uwsgi_next_upstream配置尝试将请求(默认仅限幂等的GET请求)转交给下一个upstream尝试处理后返回,这样在单一upstream由于异常负载较高超时时,其他正常的upstream可以作为backup兜底处理其超时请求,这里客户端原本等待15s超时的请求一般在5~10s内可以兜底返回。

    通过proxy_ignore_client_abort配置解决499问题?

    在网上查找资料时还有网友提出解除nginx 499问题的一个思路是设置proxy_ignore_client_abort参数,该参数默认为off,将其设置为on 后,对于客户端主动断开请求的情况,nginx会ignore而以upstream实际返回的状态为准,nginx官方文档说明如下:

    Syntax:	proxy_ignore_client_abort on | off;
    Default:	
    proxy_ignore_client_abort off;
    Context:	http, server, location
    Determines whether the connection with a proxied server should be closed when a client closes the connection without waiting for a response.

    但是在客户端主动断开连接时,设置这个参数的意义除了使nginx log中记录的状态码完全按照upstream返回确定,而非表示客户端断连的499之外,对于实际问题解决完全没有任何帮助,感觉颇有把头埋进沙子的鸵鸟风格,不知道这个参数设置到底会有什么实用的场景。

    単一のアップストリームが時々応答が遅く、非ピーク時にタイムアウトになる理由

    これは良い質問です。この問題は最近になって初めて現れました。上記の nginx の不一致の問題を解決した後、トラブルシューティングを試してください。この問題は、現象から判断すると、特定の特定のリクエストがアップストリームの CPU サージを引き起こし、応答の遅さが後続のリクエストの処理にさらに影響を及ぼし、最終的にはすべてのリクエストの応答が遅くなり、クライアント 499 がトリガーされるということです。
    nginx の不一致の問題が解決された後、単一のアップストリームで遅いタイムアウトが再び発生した場合、nginx は状況のさらなる悪化を避けるために、フェイルオーバーを通じてアップストリームの問題をすぐに解決し、最初のアクセス問題に対する GET リクエストのアップストリーム タイムアウトを回避します。また、バックアップは処理のために他の利用可能なアップストリームに転送されてから返されるため、このような例外の影響が大幅に軽減されます。
    最後に、構成を修正した後、単一のアップストリームで時折例外が発生し、一部の POST API に対して数日ごとに少数の 504 しきい値アラームがトリガーされます。問題の根本原因はまだ調査中です。

    以上が不適切な nginx 設定によって引き起こされる 499 およびフェイルオーバー メカニズムの障害の問題を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。