この記事はWeChatの公開アカウント「Su San Talks Technology」から転載したものです。著者は情熱ゆえに頑張り続けている。この記事を転載する場合は、Su San Shuo Technology Public Account までご連絡ください。 序文 以前の会社はケータリングシステムに携わっていました。正午と夕方の食事のピーク時間帯には、システムの同時実行性を過小評価しないでください。安全のため、同社では、オンライン上の問題の発生を防ぎ、タイムリーに対処できるよう、食事時間中は各部署が交代で勤務することを規定しています。 私は受注の下流業務であるバックヤードのディスプレイシステムチームに所属していました。ユーザーが注文すると、注文システムは Kafka メッセージを当社のシステムに送信します。システムはメッセージを読み取った後、ビジネス ロジック処理を実行し、注文と料理のデータを永続化して、Diancai クライアントに表示します。こうすることで、シェフは各注文に対してどの料理を準備すればよいかを把握し、一部の料理は準備が整うとシステムを通じて提供できるようになります。システムは自動的にウェイターに料理を提供するよう通知します。ウェイターが料理を提供し、料理の提供ステータスを変更すると、ユーザーはどの料理が提供され、どの料理がまだ提供されていないかを知ることができます。このシステムはキッチンからユーザーまでの効率を大幅に向上させることができます。 結局のところ、これらすべてにおいて鍵となるのはメッセージング ミドルウェアである Kafka です。これに問題があれば、バックキッチンディスプレイシステムの機能に直接影響を及ぼします。 次に、私が 2 年間 Kafka を使用する中で遭遇した落とし穴についてお話しします。 シーケンスの問題 1. メッセージの順序を保証する必要があるのはなぜですか? 当初、当社のシステムに参加している加盟店は非常に少なかったため、機能を迅速に実装するために、あまり深く考えませんでした。通信にはメッセージミドルウェア Kafka を使用しているため、注文システムはメッセージを送信する際に詳細な注文データをメッセージ本文に入れます。当社のバックキッチン表示システムがトピックをサブスクライブしている限り、関連するメッセージ データを取得して独自の業務を処理できます。 ただし、このソリューションには重要な要素があります。それは、メッセージの順序を保証する必要があるということです。 なぜ? 注文には、発注済み、支払い済み、完了、キャンセル済みなど、さまざまな状態があります。注文メッセージが読まれず、支払いまたはキャンセルのメッセージが最初に読まれるということはあり得ません。そうなると、データが混乱してしまうのではないですか? そうですね、メッセージの順序を保障する必要があるようです。 2. メッセージの順序を確保するにはどうすればよいですか? Kafka のトピックは順序付けられていないことは誰もが知っていますが、トピックには複数のパーティションが含まれており、各パーティションは順序付けられています。 このようにして、アイデアは明確になります。プロデューサーが特定のルールに従って同じパーティションにメッセージを書き込み、異なるコンシューマーが異なるパーティションからメッセージを読み取る限り、生成とコンシューマー メッセージの順序が保証されます。 これが私たちが最初にやったことです。同じ販売者番号を持つメッセージが同じパーティションに書き込まれました。トピックに 4 つのパーティションが作成され、4 つのコンシューマー ノードがデプロイされてコンシューマー グループが形成され、1 つのパーティションが 1 つのコンシューマー ノードに対応しました。理論的には、このソリューションはメッセージの順序を保証できます。 すべてが完璧に計画されていたようで、私たちは「スムーズに」オンラインになりました。 3. 予期せぬ出来事 この機能はしばらく前からオンラインになっており、当初は比較的正常でした。 しかし、良い時代は長くは続かず、すぐにユーザーから、一部の注文や料理が料理選択クライアントで表示されず、マークできないという苦情が寄せられました。 原因が分かりました。その間、会社のネットワークは不安定になることが多く、ビジネス インターフェイスがときどきタイムアウトし、ビジネス リクエストがデータベースに接続できないことがありました。 この状況は、順次メッセージングに壊滅的な打撃を与える可能性があります。 なぜそんなことを言うのですか? 注文システムが「注文」、「支払い」、「完了」という 3 つのメッセージを送信するとします。 しかし、ネットワーク上の理由により、当社のシステムは「注文」メッセージを処理できず、「注文」メッセージのデータのみが完了しており、他の種類のメッセージはステータスを更新するだけであるため、次の 2 つのメッセージのデータはデータベースに保存できません。 さらに、当時は失敗時の再試行メカニズムがなかったため、問題が拡大しました。問題は、「注文」メッセージのデータ入力が失敗すると、ユーザーは注文と料理を見ることができなくなります。 では、この緊急の問題をどう解決すればよいのでしょうか? 4. 解決プロセス 私たちの最初のアイデアは、コンシューマーがメッセージを処理するときに、処理が失敗した場合は、すぐに 3 ~ 5 回再試行するというものでした。しかし、一部のリクエストが 6 回目に成功する必要がある場合はどうなるでしょうか?再試行を続けることは不可能です。この同期再試行メカニズムにより、他の販売者からの注文メッセージの読み取りがブロックされます。 明らかに、異常発生時に上記の同期再試行メカニズムを使用すると、メッセージ コンシューマーの消費速度に重大な影響が及び、スループットが低下します。 非同期再試行メカニズムを使用する必要があるようです。 非同期再試行メカニズムを使用する場合、処理に失敗したメッセージは再試行テーブルに保存する必要があります。 しかし、すぐに新たな問題が発生します。1 つのメッセージのみが保存されている場合に、順序をどのように保証すればよいのでしょうか? メッセージを保存しても順序は保証されません。たとえば、「注文する」メッセージが失敗した場合、非同期的に再試行する時間はありません。この時点で、「支払い」メッセージは消費され、正常に消費することはできません。 この時点で、「支払い」メッセージは待機し、以前のメッセージが消費されたかどうかを時々確認する必要があります。 これを実行すると、次の 2 つの問題が発生します。
このとき、より簡単な解決策が浮かび上がりました。コンシューマーがメッセージを処理するときに、まず再試行テーブルに注文番号のデータがあるかどうかを判断します。そうであれば、現在のメッセージを再試行テーブルに直接保存します。そうでない場合は、ビジネス処理を実行します。例外が発生した場合は、メッセージを再試行テーブルに保存します。 その後、elastic-job を使用して失敗再試行メカニズムを確立しました。 7 回の再試行後にメッセージが失敗した場合、メッセージのステータスは失敗としてマークされ、開発者に通知する電子メールが送信されます。 最後に、不安定なネットワークのためにユーザーが Biacai クライアントで一部の注文や料理を表示できないという問題が解決されました。今では、商人は料理の披露をたまに遅らせることしかできませんが、料理をまったく見せないよりはずっといいです。 メッセージバックログ 営業チームのマーケティングプロモーションにより、ますます多くの販売業者が当社のシステムに参入しています。その結果、メッセージ数が増加し、消費者が処理することが困難になり、メッセージのバックログが頻繁に発生します。商人への影響は非常に直接的です。 Huacai クライアント上の注文と料理は、30 分後まで表示されない場合があります。 1~2 分の遅延ならまだ我慢できますが、気性の荒い商人の中には、メッセージの半分の遅延も我慢できず、すぐに苦情を言う人もいます。その間、私たちは注文や料理の提供が遅れているという苦情を商店主から頻繁に受けました。 サーバー ノードを追加することで問題は解決できますが、会社のコスト削減の慣行に従い、まずシステムを最適化する必要があるため、メッセージ バックログの問題を解決するための取り組みが始まりました。 1. メッセージ本文が大きすぎる Kafka は数百万 TPS をサポートすると主張していますが、プロデューサーからブローカーにメッセージを送信するにはネットワーク IO が必要であり、ブローカーはデータをディスクに書き込むためにディスク IO (書き込み操作) を必要とし、コンシューマーはブローカーからメッセージを取得するためにディスク IO (読み取り操作) とネットワーク IO を経由する必要があります。 単純なメッセージの生成および消費プロセスには、2 つのネットワーク IO と 2 つのディスク IO が必要です。メッセージ本文が大きすぎると、必然的に IO 時間の消費が増加し、Kafka の生成と消費の速度に影響します。コンシューマーが遅すぎると、メッセージのバックログが発生します。 上記の問題に加えて、メッセージ本文が大きすぎると、サーバーのディスク領域が無駄になります。注意しないとディスク容量が不足する可能性があります。 この時点で、メッセージ本文が大きすぎるという問題を最適化する必要がある段階に達しました。 どのように最適化するのでしょうか? 業務を再編成した結果、注文の中間ステータスを知る必要はなく、最終ステータスのみが必要であることがわかりました。 これは素晴らしいですね。次のように設計できます。 注文システムから送信されるメッセージ本文には、ID やステータスなどの重要な情報のみを含める必要があります。 キッチンはシステムに消費メッセージを表示した後、ID を介して注文システムの注文詳細クエリ インターフェイスを呼び出してデータを取得します。 バックキッチンの表示システムは、データベース内に注文のデータがあるかどうかを判断します。そうでない場合は保管されます。はいの場合は更新されます。 案の定、この調整後、メッセージ バックログの問題は長い間再発しませんでした。 2. 不合理なルーティングルール あまり喜ぶのは早計です。ある日の正午、ある商人が注文と料理の提供が遅れていると苦情を言いました。 Kafka トピックを確認すると、再びメッセージのバックログがあることがわかりました。 しかし、今回は少し奇妙です。すべてのパーティションにバックログ メッセージがあるわけではなく、1 つのパーティションにのみバックログ メッセージがあります。 最初は、パーティション メッセージを消費したノードに何か問題があると考えました。しかし、調査の結果、異常は見つかりませんでした。 これは変ですね、何が問題なのですか? その後、ログとデータベースを確認したところ、いくつかの販売業者の注文量が特に多かったことがわかりました。これらの販売者は偶然にも同じパーティションに割り当てられていたため、このパーティションのメッセージ量は他のパーティションのメッセージ量よりもはるかに大きくなっていました。 そのとき、メッセージを送信するときにマーチャント ID によってパーティションをルーティングするというルールが不合理であり、一部のパーティションではメッセージが多すぎて消費者が処理できない、一部のパーティションではメッセージが少なすぎて消費者が処理できないという結果になる可能性があることに気付きました。 この不均等な配布を回避するには、メッセージを送信するためのルーティング ルールを調整する必要があります。 検討した結果、ルーティングに注文番号を使用すると、より均等に分散され、1 つの注文で特に大量のメッセージが送信されるような状況にはならないと判断しました。誰かが料理を追加し続けない限り、料理を追加するには料金がかかるため、同じ注文に対するメッセージの数は実際にはそれほど多くありません。 調整後、メッセージは順序番号に応じて異なるパーティションにルーティングされ、同じ順序番号のメッセージは毎回同じパーティションに送信されます。 調整後、メッセージのバックログの問題は長い間再発しませんでした。この期間中に当社の加盟店数は急速に増加し、その数も増加しました。 3. バッチ操作による連鎖反応 同時実行性の高いシナリオでは、メッセージ バックログの問題が常に存在し、これを根本的に解決する方法は実際にはありません。表面的には解決したように見えますが、今回のような問題はいつまた発生するか分かりません。 ある日の午後、製品マネージャーがやって来てこう言いました。「何人かの商人から苦情が来ています。料理の提供が遅れているとのことで、すぐに理由を調べる必要があります。」 今回は奇妙な形で問題が発生しました。 なぜそんなことを言うのですか? まず、タイミングがちょっと変です。たいてい、ランチタイムや夕方の食事タイムに問題が起きるんですよね?今回はなぜ午後に問題が発生したのでしょうか? これまでの経験を踏まえて、Kafka のトピックのデータを直接見てみました。案の定、メッセージが溜まっていました。しかし、今回は各パーティションに消費されていない 100,000 件を超えるメッセージのバックログがあり、これは過去に圧縮されたメッセージ数の数百倍に相当します。今回のニュースの滞留は極めて異例であった。 私はすぐにサービスモニタリングをチェックして、消費者が電話を切ったかどうかを確認しましたが、幸運にもまだ切っていませんでした。サービスログを再度確認しましたが、異常は見つかりませんでした。この時、私は少し混乱していたので、思い切って注文グループに午後に何が起こったのか尋ねてみました。午後にプロモーションがあり、一部の販売者の注文情報を一括更新するジョブが実行されたとのことでした。 この瞬間、私は突然夢から目覚めました。この問題は、JOB でメッセージをバッチで送信したことが原因でした。なぜ通知しなかったのですか?これは本当にぼったくりだ。 問題の原因はわかっているものの、蓄積された数十万件のメッセージにどう対処すればよいのでしょうか。 現時点ではパーティション数を直接増やすことはできません。履歴メッセージは 4 つの固定パーティションに保存され、新しいメッセージのみが新しいパーティションに送信されます。私たちの焦点は、既存のパーティションの処理にあります。 サービス ノードを直接追加することも機能しません。これは、Kafka では、同じグループ内の複数のパーティションを 1 つのコンシューマーが使用することは許可されますが、1 つのパーティションを同じグループ内の複数のコンシューマーが使用することは許可されないため、リソースの無駄が生じる可能性があるためです。 マルチスレッドしか使えないようです。 問題を早急に解決するために、スレッド プールを使用してメッセージを処理するように変更し、コア スレッドと最大スレッドを 50 に設定しました。 調整後、メッセージのバックログは減少し続けました。 しかし、このとき、さらに深刻な問題が発生しました。注文システムの 2 つのノードがダウンしているという警告メールを受け取ったのです。 すぐに、注文グループの同僚が私のところに来て、注文クエリ インターフェイスを呼び出すシステムの同時実行性が突然予想よりも数倍増加し、2 つのサービス ノードがクラッシュしたと言いました。クエリ機能を別のサービスに統合し、6 つのノードを展開しました。 2 つのノードに障害が発生した場合、何もアクションを取らなければ他の 4 つのノードにも障害が発生します。オーダーサービスは同社の最もコアなサービスといえる。失敗すれば会社は莫大な損失を被ることになり、状況は極めて緊急です。 この問題を解決するには、まずスレッドの数を減らすしかありません。 幸いなことに、スレッド数は Zookeeper を通じて動的に調整できます。コアスレッド数を8に調整し、コアスレッド数を10に変更しました。 その後、運用保守チームは、注文サービスによって損傷した 2 つのノードを再起動し、正常な状態に回復しました。念のため、さらに 2 つのノードが追加されました。注文サービスに支障が出ないよう、電流消費速度は一定に保たれています。キッチン表示システムのメッセージバックログ問題は1時間後に正常に戻りました。 その後、検討会議を開催し、以下の結論に達しました。
ちなみに、メッセージの順序を厳密に保証する必要があるシナリオでは、スレッド プールを複数のキューに変更し、各キューを 1 つのスレッドで処理することができます。 4. テーブルが大きすぎる メッセージ バックログの問題が再発しないようにするために、コンシューマーは引き続きマルチスレッドを使用してメッセージを処理します。 しかし、ある日の正午になっても、Kafka トピック メッセージのバックログがあることを知らせる警告メールが大量に届きました。原因を調査していたところ、商品担当者がやって来てこう言いました。「別の商人から料理の配達が遅れていると苦情が来ています。調べてみましょう。」今度は彼女は少しイライラしているようだった。彼女は何度も最適化しましたが、それでも同じ問題が発生しました。 素人の視点から見ると、なぜ同じ問題を解決できないのでしょうか? 実際のところ、彼らはテクノロジーが感じる痛みを知りません。 表面的には、問題の症状は食事の遅延という同じもので、メッセージのバックログが原因であることはわかっていました。しかし、彼らはその深い理由を知りません。実際には、メッセージのバックログが発生する理由は多数あります。これは、メッセージ ミドルウェアを使用する場合によくある問題である可能性があります。 私は黙ったまま、我慢して原因を突き止めることしかできませんでした。 後でログを確認したところ、コンシューマーがメッセージを消費するのに最大 2 秒かかることがわかりました。以前は 500 ミリ秒でしたが、なぜ今は 2 秒になったのでしょうか? 不思議ですね。消費者コードは大幅に調整されていません。なぜこのようなことが起こるのでしょうか? オンラインメニュー表を確認したところ、1つの表のデータ量が数千万件に達していることがわかりました。他のメニュー表についても同様です。現在、1 つのテーブルに保存されるデータが多すぎます。 私たちのチームはビジネスを整理し、過去 3 日間の料理のみをクライアントに表示する必要があることを発見しました。 これは簡単にできます。サーバーに冗長データが保存されているため、冗長データをテーブルにアーカイブすることもできます。そのため、DBA はデータのアーカイブに協力し、過去 7 日間のデータのみを保存しました。 この調整により、メッセージのバックログ問題は解決され、状況は以前の平穏に戻りました。 主キーの競合 すぐに喜びすぎないでください。他にも次のような問題があります: アラーム メールには、データベース例外が頻繁に報告されます: キー 'PRIMARY' のエントリ '6' が重複しており、主キーの競合が発生しています。 この問題は通常、同じ主キーを持つ SQL が 2 つ以上あり、データが同時に挿入される場合に発生します。最初の挿入が成功した後、2 番目の挿入を実行すると主キーの競合が報告されます。テーブルの主キーは一意であり、重複は許可されません。 コードを注意深く確認したところ、コード ロジックではまず主キーに基づいてテーブル内に順序が存在するかどうかを照会していることがわかりました。存在する場合は、ステータスが更新されます。存在しない場合は、データが挿入されます。問題ありません。 この判断は同時実行性が大きくない場合に役立ちます。ただし、同時実行性の高いシナリオで、2 つの要求が同時に順序が存在しないことを検出し、一方の要求が最初にデータを挿入し、もう一方の要求が後でデータを挿入すると、主キー競合例外が発生します。 この問題を解決する最も一般的な方法は、ロックを追加することです。 最初はそう思いました。悲観的ロックをデータベースに追加することは、パフォーマンスに大きな影響を与えるため、決して良い考えではありません。データベースへの楽観ロックの追加は、バージョン番号の判断に基づいて行われます。これは通常、更新操作に使用され、基本的にこのような挿入操作には使用されません。 残っているのは分散ロックを使用することだけです。当社のシステムは Redis を使用しているため、Redis に基づく分散ロックを追加して注文番号をロックできます。 しかし、よく考えてみると:
したがって、分散ロックも使用するつもりはありません。 代わりに、MySQL の INSERT INTO ... ON DUPLICATE KEY UPDATE 構文を使用することを選択します。
まずテーブルにデータを挿入し、主キーの競合がある場合はフィールドを更新します。 以前の挿入ステートメントを変更した後、主キーの競合の問題は発生しなくなりました。 データベースのマスタースレーブ遅延 しばらくして、ある日、注文後、Huacai クライアントで注文内容を確認できたものの、表示される料理が不完全で、注文内容や料理データさえ確認できないことがあるという苦情が商店主から寄せられました。 この問題は以前の問題とは異なります。過去の経験に基づいて、まず Kafka トピックにメッセージのバックログがあるかどうかを確認しますが、今回はバックログはありません。 サービス ログを再度確認したところ、注文システム インターフェイスから返されたデータの一部が空であり、注文データのみが返され、料理データが返されていないことがわかりました。 これは非常に奇妙だったので、私はすぐに注文グループの同僚を探しに行きました。彼らはサービスを注意深くチェックしましたが、問題は見つかりませんでした。この時、私たちはデータベースに問題があるかもしれないと考え、一緒にDBAを探しに行きました。予想通り、DBA は、ネットワーク上の理由により、マスター データベースからスレーブ データベースへのデータの同期に遅延が発生することがあり、その遅延は 3 秒に及ぶこともあったことを発見しました。 ビジネス プロセスでメッセージの送信から消費までに 3 秒未満かかる場合、注文詳細クエリ インターフェイスを呼び出すときに、データが見つからないか、見つかったデータが最新ではない可能性があります。 この問題は非常に深刻であり、データにエラーが直接発生することになります。 この問題を解決するために、再試行メカニズムも追加しました。データを照会するためにインターフェースを呼び出すときに、返されたデータが空の場合、または料理なしで注文のみが返された場合は、再試行テーブルに追加されます。 調整後、商人らが不満を訴えていた問題は解決した。 繰り返しの摂取 Kafka は、メッセージを消費するときに 3 つのモードをサポートします。
Kafka のデフォルト モードは少なくとも 1 回ですが、このモードでは重複消費の問題が発生する可能性があるため、ビジネス ロジックはべき等になるように設計する必要があります。 私たちのビジネス シナリオでは、INSERT INTO ...ON DUPLICATE KEY UPDATE 構文を使用してデータを保存します。キーが存在しない場合は挿入されます。キーが存在する場合は更新されます。このメソッドは当然ながらべき等性をサポートします。 多環境消費問題 当時のオンライン環境は、pre(プレリリース環境)と prod(本番環境)に分かれていました。 2 つの環境は同じデータベースと同じ Kafka クラスターを共有していました。 Kafka トピックを構成するときは、異なる環境を区別するためにプレフィックスを追加する必要があることに注意してください。異なる環境でメッセージが混在するのを防ぐため、事前環境の名前は pre_order のように pre_ で始まり、実稼働環境の名前は prod_order のように prod_ で始まります。 しかし、あるとき、運用保守チームが pre 環境でノードを切り替えてトピックを構成したとき、ミスを犯して prod トピックを構成してしまいました。ちょうどその日、事前環境に新しい機能が追加されました。結果は悲惨なものでした。 prod からの一部のメッセージは、pre 環境のコンシューマーによって消費されました。しかし、メッセージ本文の調整により、事前環境のコンシューマーはメッセージを処理できずにいました。 その結果、実稼働環境で一部のメッセージが失われました。幸いなことに、実稼働環境のコンシューマーは、オフセットをリセットし、メッセージのその部分を再度読み取ることで、大きな損失を出さずに最終的に問題を解決しました。 追記 上記の問題に加えて、次のような問題にも遭遇しました。
これら 2 つの質問は説明するのが少し複雑なので、1 つずつリストすることはしません。興味のある友人は私の公式アカウントをフォローし、WeChatを追加して私とプライベートにチャットすることができます。 過去 2 年間、メッセージ ミドルウェア Kafka を使用した経験に非常に感謝しています。多くの問題に遭遇し、多くの落とし穴に陥り、多くの回り道をしましたが、そのおかげで本当に多くの貴重な経験を積み、急速に成長することができました。 実際、Kafka は非常に優れたメッセージ ミドルウェアです。私が遭遇した問題のほとんどは、Kafka 自体の問題ではありませんでした (CPU 使用率が 100% になったのはバグによるものでしたが)。 |
<<: 調査: マルチクラウド導入のメリット、障壁、最も人気のあるクラウド プラットフォーム
>>: クラウドコンピューティングとは何ですか?理解するための1つの記事
私は専門知識があまりない単なるアマチュア SEO 担当者なので、継続的な探求を通じてのみ成長すること...
[hostodo]専門的な情報を得るために、海外のウェブサイトにアクセスするための x 機能を実装す...
タレントサイトといえば、現在多くの個人ウェブマスターが管理が難しいと語っています。タレントサイトを収...
インターネット業界では、いくつかの単語の略語を使用することを好みます。クラウド コンピューティング業...
9月19日、杭州雲奇カンファレンスにおいて、中国大手のIoTおよびIT可視化管理企業であるYouke...
vpsdime の最後のプロモーションは 2018 年 7 月 22 日でした。現在、年間 20 ド...
mycustomhosting は 2009 年に設立されたと主張する VPS プロバイダーですが、...
ハイパーリンクのないURLが最適化に効果的かどうかを2つの側面から分析するSEO担当者は、説明的なテ...
2021年6月23日、徐州市人民政府が主催する「2021年中国(徐州)第5回人工知能会議及びデジタル...
[[442894]] 1. 分類から本質を見極める製品を分類することは、製品の本質を検討し理解するこ...
物理的なセキュリティに関する潜在的な固有の課題に加えて、エッジ コンピューティングの場所ではさらにデ...
以前、Hao Xinが「SEOer PKグラフィックデザイナー」という記事を書きました。今日(201...
中国で唯一、China Telecom の AS4809 (cn2 gia) に匹敵するネットワーク...
2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています「フランス...