ホームページ  >  記事  >  バックエンド開発  >  リクエストごとのエントリの処理方法 - Go! を使用して

リクエストごとのエントリの処理方法 - Go! を使用して

Barbara Streisand
Barbara Streisandオリジナル
2024-11-05 04:45:02632ブラウズ

How I processed over Entries per request - With Go!

始める前に、私が誰なのか、そしてなぜそれがこの場合重要なのかについて少し説明します。私は Notebook Manufacturing Company のソフトウェア開発者で、過去 2 年間ここで働いています。私が現在いるチームでは、私が唯一の開発者で、データ パイプライン、自動化、Go と Grafana を使用した監視システムの作成、監視、保守を担当しています。
もう一つ付け加えさせていただくと、前年、私はインターンをしていました。そして私は独学です。

わかりましたが、なぜそれが重要なのでしょうか?

そうですね、私には上級開発者もいなかったし、自分だけでは解決できない障害や問題に直面したときに私を導いてくれる内部からの指導もありませんでした。それが私がこの記事を書いている主な理由です。とても楽しかったのでシェアしたいと思います。それは素晴らしいソフトウェアや画期的なものではありませんが、物事をゼロから構築することが可能であることを思い出させてくれます。 100 万人に 1 人の 10x 開発者である必要はありません。

追伸: ちなみに私は neovim を使っています。

100万行

この数字は誇張か何かのように見えるかもしれません、そして、そうであってほしいと思いますが、そうではありません。ほら、製造業のコンテキストで作業する場合、部品ごとに多くの製造キューのすべてのポイントを追跡する必要があるときに、必要なアイテムの数と、それによって生成されるデータの量が考慮されないことがあります。
それが小さなネジであっても、前世代の CPU であっても、タイムスタンプを使用して追跡する必要があります!
つまり、はい、生成されるデータの量は適切であり、それは単なる始まりにすぎません。 (私の観点から) この問題をクールにしているその他の優れた点は次のとおりです:

  • このデータのソースは圧縮などされていなかったため、平均リクエスト時間は良い日でも 2 ~ 3 分近くかかりました。ソースに遅れがあると、5 分以上かかる可能性があります (私よりも古い Java システムです)。
  • 形式については、XML 応答を処理するか CSV 応答を処理するかを選択できましたが、もちろん CSV を選択しました。私はマゾヒストではありません。
  • また、データを取得する間隔を最低 1 時間で選択する必要がありました (いいえ、理由はわかりません。はい、マイナーな間隔を試してみました)。
  • ソースには遅延読み込みやキャッシュ システムがなかったため、2 つの等しいリクエストは異なる時間で同じ応答を生成します。
  • そして最後に、事態をさらに盛り上げるために、これは SQL Server Reporting Services として知られる Microsoft の製品であり、独自のあいまいな注意事項があります。ああ、データベースは MSSQL を使用する必要があるのですが、これが面倒です。

これが全体のコンテキストです。ここから楽しい部分が始まります。

最初の反復 - DMP

私がソフトウェアに取り組む方法は非常にシンプルです。醜くてほとんど機能しないものにします ->見た目はそのままにして機能を改善 ->まだ醜いですが、より最適化されています ->美しく、最適化され、機能します ->そして、マネージャーは、あなたの仕事があまりにも優れていたため、ソースがそれを処理できず、機能停止が発生したと言いました。 FML.

とにかく、DMP は Dumb Mode Protocol の略です。できる限り愚かな方法で動作させるだけです。つまり、かろうじて動作するようにすることを意味します。

そのため、最初のラウンドでは、私の目標は単純で、認証、リクエストの作成、データの解析、データベースへの送信でした。とてもシンプルですよね?そして、紙面では、使用しなければならなかった認証および認可方法が ntlmssp であることを発見するまで、そうでした。これは、存在を知らなかったチャレンジ/レスポンス認証方法である ntlmssp でした。実際のところ、それを見つけるにはレガシー .NET 4 コードを使用する必要がありました。 C# をやったことがありません。
私よりも古いレガシーコードを調べた後、コードを理解するのに苦労しました。なぜなら、コードを書いた人は、何らかの理由で、それを抽象化、コンストラクター、および OOP の 5 層に隠すのが良い考えだと考えたからです。楽しくない、謙虚な経験。そして、それを機能させました。 1 時間後、どうやら送信元にレート制限があるため、ユーザーがブロックされました。ただし、キャッシュや遅延読み込みはありません。

すべてが終わったら、クエリ パラメータを渡してデータを取得するだけで済み、ソースを複雑にする必要はありません。わかりました。クエリ パラメーターのドキュメントを調べてみましょう。

...

この時点では、すべてのことを考慮すると、おそらくあなたの推測は正しかったでしょう。ドキュメンテーション?これはシリコンバレーでのみ提供されるある種のエキゾチックな食べ物ですか?
とにかく、知恵を絞った結果、この SQL Server Reporting Services のサイト/インターフェイスを検査することにしました。そして、嬉しいことに、そして憎しみにも、フィルターとその値を知る方法があったのです。

ページ内の (「すべて」) フィルターで、たとえばすべての製造ラインを選択することは、すべてのボックスをチェックすることを抽象化したものにすぎないことだけがわかります。 OK、フィルターをコピーしてクエリ文字列に入れ、エンコードして満足しましょう!

うまくいきました!あるいはそう思いました。

ユーザーがブロックされたと私が言ったことを覚えていますか?そうですね、そのようなタスクを実行するための管理者権限があり、経営幹部からその実行を許可されている (実際、それは経営幹部から要求されています) だけでは、複数の要求を実行するには十分ではなかったようです。ユーザーはブロックされましたが、マネージャーにとっては「何も起こらなかった」と言っているのと同じでした。幸いなことに、私はかなり早くブロックを解除することができました。

それまでの間、私はこのプロジェクトの残りの部分にどのように取り組むかを検討することにしました。この時点で私はすでにそのサンプルを持っており、それは投稿のタイトルどおりのものでした。 7 桁を見て、このデータ量を扱った経験がまったくなかった私にそれができるかどうか自問しました。

自分のアイデアを形にするために、excalidraw に参加して、やりたいことをデザインしました。ワーカー プールの概念は知っていましたが、これまで実装したことはありませんでした。そこで、それについて読んで、いくつかの実装を見て、非常に助けてくれた友人に尋ねました。彼は私にいなかった先輩です。

疑似コードで書き出した後、次のように思いました。

「おお、これはなかなかいいですね。これらすべてのチャネルとゴルーチンでメモリ リークが発生することは確実にありませんね?」

わかりました、その言葉通りではなかったかもしれませんが、その通りでした。最初の反復を実行し、(ブロックされることなく) リクエストを処理し、メモリにロードした後、ワーカー プールの概念を適用しました。

そしてBSODに直面しました。面白いことに、それはクラウドストライクのストライキと同じ日でした。私はまさか自分が工場に大規模な停止を引き起こしたとは思っていませんでした。
もちろん、仕事には Windows を使用する必要がありますが、心配する必要はありません。 WSL2を使用しています。

最初のスタック オーバーフローの膨大なスタック トラックを調べた後、間違いに気づきました。データをコンシューマ チャネルに送信するときに、主に主キーの違反、または単にエラーを適切に処理していなかったことが原因で、一部のデータでエラーが発生する可能性があることを考慮していませんでした。

教訓として、エラー チャネルを使用して重大な問題を回避してください。そして、単純な文字列チェック (醜いですが機能します) を介してエラーを処理するか、単にトップレベルでログを記録するだけで、満足することができます。直面したエラーは重大なものではなかったので、続行することができました。

2 回目の反復。メモリリーク。

このステップで発生したメモリ リークの量を見て、C を実行しているのではないかと思いました。しかし、それは単なる大きなスキルの問題でした。
とにかく、あなたは次のように考えているかもしれません。

「このような単純なプロセスで、どのようにしてメモリ リークが発生したのですか?」

それは簡単です。私は学び、試していました。

このステップでの主な問題は、私が素朴にデータが適切に挿入されていることを確認できず、主キーの違反の量が私の記憶を侵害していたことでした。これは解決方法がわかっていた問題です。データをキャッシュしましょう!
待ってください。各行が一意であることを考慮して、各行の一意の識別子を作成するにはどうすればよいですか?

さて、これは多くの人が私を笑うところでしょう。なぜなら、公平を期すために、それが私を最もイライラさせる部分だからです。現在の情報行を単純に結合してハッシュ関数に解析すると、そのハッシュがマップ内のキーになりました。

「なぜ Redis ではないのですか?」 - あなたは自問しているかもしれません。

それは単純です、官僚主義。小さな会社ではありません。実際のところ、おそらく皆さんの多くは自社製のラップトップを使用しているでしょう。 Redis インスタンスをリクエストするという単純な行為には、少なくとも 3 営業日、4 回の会議、官僚制の神への犠牲、そしてその理由と方法を説明する完全な文書が必要になります。
そうですね、単純なハッシュ マップを使用して、最初の実行前に事前初期化しましょう。全体的な読み込み時間は長くなりますが、リクエストよりも早くなります。

そうすることで、新しいモーターを持ったかのように全体的なプロセスが改善され、メモリリークが停止し、バッチが毎回失敗することもなくなり、エラーや切断の量も減少しました。とても良いことですよね?そうですよね?

もう、何かがごちゃ混ぜになっていることがわかります。

3回目の反復。人生は良いものかもしれない。

私が考慮していなかった点の 1 つは、バッチ挿入を行うことでデータが検証されるという事実でした。流れを簡単に表すと次のようになります。

データの取得 ->データのハッシュがハッシュマップに存在するかどうかを確認します。バッチ&挿入

それで、何が問題なのでしょうか?では、バッチ内の 1 つの挿入が失敗した場合はどうなるでしょうか。エントリなしで再試行されるのでしょうか?その場合、システムを混乱させたり、ワーカー プール実装の利点を失わずに、どれくらいの再試行を行うことができますか?
それを知る方法はたった一つ!確認してみましょう。
1 つ追加できる点は、このソースが 25 列を超える列を返したという事実です。そのため、MSSQL の制限である 2100 パラメーターを超えないよう、バッチごとに挿入するデータの量に注意する必要がありました。

この時点で、私はすでに、限られたリソースで実稼働スペースを模倣する Docker コンテナーで物事を実行していました。コンテキストを追加するために、このプロセスは 1 GB の RAM と約 0.5 CPU で実行されました。もっと多くのリソースを割り当てることもできましたが、それは単に強引なやり方になってしまいます。
この新しい反復をコンテナー内で実行し、いくつかのタイムスタンプを追加し、後で分析するためにファイルにログアウトします。再試行の量により、約 5 分増加することがわかりました。これは機能しません。「ダーティ」エントリを削除するという選択肢はありませんでした。

4 回目の反復。人生は良いものです。

この問題を解決するために、労働者の数を増やしました。私は約 50 人ほどのワーカーを使用していましたが、ThePriimagen の最上位にいる Discord ユーザーのランダムな推測のおかげで、ワーカーを 1000 人に増やし、各ワーカーがトランザクション内の各エントリを検証するようにしました。トランザクションが失敗した場合は、単にロールバックしました。
そうすることで、中心的な問題を解決し、このプロセス全体の速度を全体的に向上させることができました。今度は、それを prod に入れて監視するときが来ました。なぜなら、prod ゴブリンがソフトウェアを混乱させる可能性があるからです。 (スキルの問題とも呼ばれますが、この名前は禁止されています。)

このシステムのフェッチ間隔を減らすことが要求され、ほぼリアルタイム (つまり、遅延に気付かないほど十分な速度) にすることが要求されることを知って、今回はもう少し堅牢な新しいコンテナを作成しました。負荷に耐えられるかどうかを確認し、間隔を 3 分に設定します。平均フェッチ時間を考慮すると、多少の重複はあるかもしれませんが、実際にどうなるか見てみたかったのです。

一晩実行し、後で確認するために結果をログに記録しました。そして驚いたことに、勤務時間が終了する前にマネージャーから電話を受けました。言っておきますが、彼は技術者などではありません。

「ねえ、[私が公開できないシステム名] と対話するものを「私たち」は導入しましたか?」

「はい、リクエストに応じてリアルタイムでデータ取得システムを導入しました。なぜですか?」

「うーん、止めてもらえますか? 停電が発生したため、ここでは作業できません。」_

これは、ギャディとレントルマン、まったく別のレベルの画期的な製品です。文字通り、20 以上の生産ラインを 1 分間ほど停止しました。とにかく、システムを停止したところ、すべてが正常に戻りました。
翌日、取得の間隔を 3 分ではなく 30 分に増やすように言われました。まあ、大丈夫でしょう。嘘はつきません。最高速度でのパフォーマンスを見ることができなくなるのは少し残念でしたが、少なくとも、うまく機能するようになりました。

この新しいシステムにより、このデータを使用したレポートの平均更新時間は 8 ~ 10 秒に短縮され、その結果、管理者によって同じソースのレポートが同様に要求されることが多くなりました。良い仕事には、より多くの仕事が与えられます!

考慮事項

それは主に、Go が実際にどれほど強力であるかを実感させられたという事実により、楽しい経験でした。 Google Chrome よりも少ないメモリ、Microsoft アプリよりも少ないストレージ、Windows 電卓よりも少ない CPU パワーを使用して、文字通り総当たりで処理を進めていた古いプロセスを改善することができました (文字通り、挿入前にデータベース内の各行をチェックしていました。前の人がそれが良いアイデアだとどう考えたのかはわかりません。)本当に楽しかったです

とにかく、このプロセス全体を通してあなたの考えを自由に共有してください。どのようにアプローチするか、別の方法で何をしただろうか。私には開発者の同僚がいないので、それについての意見をもっと知りたいです。

以上がリクエストごとのエントリの処理方法 - Go! を使用しての詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。