「すでに回答済み」のトピックのように聞こえるかもしれないタイトルで申し訳ありませんが、私のケースはユニークだと思います。
また、これは私の最初の投稿です。問題がサーバー管理側にあるのか、Laravel の構成側にあるのかわからないため、適切なチャンネルにいない場合はお詫び申し上げます。
修正されたと思っていた Horizon / Predis / HAProxy の問題を解決する方法について、いくつかの新鮮なアイデアを得ようとしていますが、再び表示されます。
環境に関するいくつかの詳細
- 2x Apache サーバー: PHP バージョン 7.2.29-1+ubuntu18.04.1+deb.sury.org+1
- スレッドセーフが無効になっており、FPM を使用しています
- 単純なマスター/スレーブ セットアップを使用する 2 台の Redis サーバー (高可用性なし、センチネルなし): redis バージョン 4.0.9
- HAProxy バージョン 1.9 による負荷分散
ライブラリ
- laravel/フレームワーク: 6.14.0
- laravel/horizon": 3.7.2
- レディス/プレディス: 1.1.1
ホライズン構成
Horizon デーモンは、Supervisor によって管理されます。
これは の Redis クライアント構成ですconfig/database.php
。
'redis' => [
'client' => 'predis',
'options' => [
'prefix' => strtoupper(env('APP_NAME') . ':')
],
'default' => [
'host' => env('REDIS_HOST'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT'),
'database' => env('REDIS_DB'),
'read_write_timeout' => -1
],
...
これは の Redis 接続構成ですconfig/queue.php
。
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 110
],
'redis-long-run' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'long-running-queue'),
'retry_after' => 3620
],
ご覧のとおり、同じ物理 Redis サーバーに対して 2 つの接続が定義されています。アプリケーションは、2 つの異なるタイプのジョブにキューを使用します。
- ブロードキャスト、通知、一部の Artisan コマンド呼び出しなどの高速/短いプロセス。これらは、タイムアウト設定が低い最初の接続構成を使用します。
- 基本的に、Snowflake DB (DB のようなクラウドベースの SQL) で大量のデータをクエリしたり、Solr サーバーでドキュメントを更新/挿入したりする、長時間実行されるプロセス。これらのプロセスは、完了するまでにかなりの時間がかかる可能性があるため、2 番目の接続構成を使用します (通常、Snowflake からの読み取りと Solr への書き込みを組み合わせたものでは約 20 分)。
アプリケーションは、私の会社での個人使用を目的としたビジネス Web アプリケーションであり、負荷はかなり小さい (1 日あたり約 200 ジョブがキューに入れられる) が、長時間実行されるプロセスはビジネスにとって重要です。ジョブの失敗や二重実行は受け入れられません。
これはconfig/horizon.php
ファイルです:
'environments' => [
'production' => [
'supervisor-default' => [
'connection' => 'redis',
'queue' => ['live-rules', 'solr-cmd', 'default'],
'balance' => 'simple',
'processes' => 3,
// must be lower than /config/queue.php > 'connections.redis'
'timeout' => 90,
'tries' => 3,
],
'supervisor-long-run' => [
'connection' => 'redis-long-run',
'queue' => ['long-running-queue', 'solr-sync'],
'balance' => 'simple',
'processes' => 5,
// must be lower than /config/queue.php > 'connections.redis-long-run'
'timeout' => 3600,
'tries' => 10,
],
],
'staging' => [
...
初期問題<解決済み>
今年の初めに稼働したとき、すぐに実行時間の長いキュー接続で実行されているジョブに問題が発生しました。
Error while reading line from the server. [tcp://redis_host:6379]
エラーが左右に飛び出し始めました。
これらは、タスクが実際には成功したにもかかわらず、最終的に失敗としてマークされるまで、ジョブが保留状態でスタックすることに変換されました。
当時、アプリケーションの長時間実行プロセスは Snowflake SELECT クエリに限定されていました。
Laravel Horizon の github の問題や SO のトピックに関する多数の投稿を調べ、提案をテストした結果、運が悪かったため、90 秒後に接続を閉じたロード バランサーが原因であることが最終的にわかりました。
Redis には 300 秒の tcp-keepalive デフォルト設定パラメーターがあるため、HAProxy の設定を調整して 310 秒で閉じるようにしました。-、しばらくの間、すべてがうまくいきました。
これは、最近のアプリケーションの HAProxy の構成です。
listen PROD-redis
bind 0.0.0.0:6379
mode tcp
option tcplog
option tcp-check
balance leastconn
timeout connect 10s
timeout client 310s
timeout server 310s
server 1 192.168.12.34:6379 check inter 5s rise 2 fall 3
server 2 192.168.43.21:6379 check inter 5s rise 2 fall 3 backup
新たな問題(初期生まれ変わり?)
数か月後にアプリケーションが進化し、Snowflake からバッチで読み取りと生成を行って Solr 更新クエリを作成するジョブができました。Solr クライアントは solarium/solarium であり、addBuffered プラグインを使用します。
これは、ロード バランシングのない運用前環境で問題なく機能しました。
次に本番環境に移行すると、Redis 接続の問題が予期せず再び発生しましたが、今回は HAProxy が適切にセットアップされています。
Redis でキーを監視すると、これらのジョブが実際に予約されていることがわかりますが、しばらくすると遅延状態になり、ジョブのタイムアウトに達すると再試行されるのを待ちます。
最終的に失敗としてマークされるまで、ジョブの最大試行回数を通過し、complete
フラグを取得しないため x 回実行し、環境に不必要なストレスを与え、実際にジョブが DID のときにリソースを消費するため、これは実際の問題です。最初の試行で成功します。
これは、HAProxy のログから取得したものです。
Jun 26 11:35:43 apache_host haproxy[215280]: 127.0.0.1:42660 [26/Jun/2020:11:29:02.454] PROD-redis PROD-redis/redis_host 1/0/401323 61 cD 27/16/15/15/0 0/0
Jun 26 11:37:18 apache_host haproxy[215280]: 127.0.0.1:54352 [26/Jun/2020:11:28:23.409] PROD-redis PROD-redis/redis_host 1/0/535191 3875 cD 24/15/14/14/0 0/0
このcD
部分は、haProxy のドキュメントによると、興味深い情報です。
c : the client-side timeout expired while waiting for the client to send or receive data.
D : the session was in the DATA phase.
このようなログは他にもあり、日付からわかるように、接続が確立されてから閉じる瞬間までの遅延に明らかなパターンはありません。
そこにたどり着く前に、次のものがあります。
- Redis バージョン 5.0.3 サーバーに切り替えました: 同じ問題。
- 式から HAProxy を削除し、クライアントと Redis の間の直接接続を確立しました。問題なく動作します。
問題を完全に把握して修正する方法について、私は少し途方に暮れています。クライアント側のタイムアウトに関する HAProxy ログに戻ると、クライアント構成のどこが間違っている可能性があり、次に何を試すべきか疑問に思います。
多分誰かがここに提案を持ってくるでしょうか?読んでくれてありがとう。