iLogtail に従って設計パターンを学習してください

デザイン パターンはソフトウェア開発の経験を要約した重要な情報であり、Gang of Four (GoF) によって提案された古典的なデザイン パターンはデザイン パターンの「バイブル」として知られています。しかし、デザイン パターンは抽象的かつ理論的に表現されることが多く、初心者や実務経験の少ない開発者にとって、デザイン パターンを直接学ぶのは退屈なことが多いです。

多くの場合、実用的なアプリケーション シナリオとともにデザイン パターンを簡単に紹介しようとする書籍や記事が市場やインターネット上に存在します。ただし、これらの資料にリストされている例やアプリケーションの実践は、多くの場合、構築された仮想シーンであり、運用レベルのソフトウェアの実際のアプリケーションが欠けています。ソフトウェア理論で最も重要なことは、学んだことを応用することですが、実際の実稼働レベルのコードを学ぶ機会はありますか?

iLogtail は、Alibaba Cloud Log Service (SLS) チームが自社開発した観測可能なデータ コレクターであり、Github でオープンソース化されており、その中心的な位置付けは、開発者が統合されたデータ収集レイヤーを構築するのを支援することです。iLogtail は長年にわたる技術進化の過程で、さまざまな設計パターンの適用を試みており、これらの設計パターンの適用により、ソフトウェアの品質と保守性が大幅に向上しました。この記事では、iLogtail プロジェクトを組み合わせて、いくつかの一般的な設計パターンの技術原則を実用的な観点から説明します。また、いくつかの iLogtail Golang アーキテクチャのアップグレードと最適化をしてくれた多くの ByteDance の学生にも感謝したいと思います。

デザインパターンを学ぶのが退屈だと感じたら、ぜひ iLogtail を学びに来てください! あらゆる形式のコミュニティ ディスカッションや交流への参加を歓迎します。デザイン パターンを学ぶことも非常に興味深いことであることがわかると思います。

創作パターン

作成パターンの役割は、オブジェクトを作成するための一般的なソリューションを提供し、オブジェクト作成の詳細を非表示にすることです。オブジェクトを作成する場合、最もよく知られているのは、オブジェクトを作成してから、関連するプロパティを設定することです。ただし、多くのシナリオでは、特にさまざまな複雑なクラスを作成するシナリオでは、オブジェクトを作成するためのより使いやすい方法をアプリケーション側に提供する必要があります。

シングルトンパターン

パターン紹介

シングルトン モードとは、クラスの一意性を確保するために、システム ライフ サイクル全体でクラスのインスタンスを 1 つだけ生成できるようにすることを指します。一部のリソース管理シナリオ (構成管理など) では、システムの全体的な動作を調整するのに役立つグローバル オブジェクトが必要になることがよくあります。

iLogtail の練習

iLogtail では、収集構成管理はユーザー収集構成と内部収集タスクを接続する上で重要な役割を果たし、ユーザー収集構成をロードして分析することにより、特定の収集タスクが確立されます。

プロセス レベルの管理メカニズムとして、ConfigManager はシングルトン モードに非常に適しています。iLogtail が起動すると、最初にすべてのコレクション構成がロードされ、操作中に変更されたコレクション構成の動的なロードがサポートされます。シングルトン モードを通じて、複数のインスタンス間の状態同期の問題を効果的に回避でき、各モジュールが呼び出すのに便利な統一されたグローバル インターフェイスも提供されます。

class ConfigManager : public ConfigManagerBase {
public:
    static ConfigManager* GetInstance() {
        static ConfigManager* ptr = new ConfigManager();
        return ptr;
    }

// 构造、析构、拷贝构造、赋值构造等均为私有,防止构造多个对象
private:
    ConfigManager();
    virtual ~ConfigManager(); 
    ConfigManager(const ConfigManager&) = delete;
    ConfigManager& operator=(const ConfigManager&) = delete;
    ConfigManager(ConfigManager&&) = delete;
    ConfigManager& operator=(ConfigManager&&) = delete;
};

GetInstance() 関数はシングルトン モードの鍵であり、静的変数と静的関数を使用して、アプリケーション内に ConfigManager クラスのインスタンスが 1 つだけ存在するようにします。コピーまたは代入によって複数の ConfigManager オブジェクトがインスタンス化されないようにするには、コピー コンストラクターと代入演算子をプライベートとして定義し、削除対象としてマークします。

同時に、C++11 標準の Magic Static 機能を使用します。初期化中に変数が同時に宣言ステートメントに入ると、同時スレッドはブロックされて初期化が完了するまで待機し、同時プログラムのスレッドの安全性が確保されます。

変数の初期化中に制御が同時に宣言に入った場合、同時実行は初期化の完了を待機します。

工場パターン

パターン紹介

ファクトリ パターンは、オブジェクトを作成するための最適な方法を提供します。オブジェクトを作成するとき、作成ロジックはクライアントには公開されません。クライアントは、作成するオブジェクトをファクトリ クラスに伝えるだけでよく、残りの作業はファクトリ クラスによって実行されます。

iLogtail の練習

多くの監視可能なデータ型の収集と処理の要件を満たすために、ログ、メトリック、およびトレースは iLogtail C++ パイプラインで定義され、パイプライン イベントはパイプライン データ フローの一般的な形式として抽象化されます。Pipeline のデータ フローの基本単位として、Pipeline Event には多数の Event アプリケーションが関与することが多いため、Pipeline Event ファクトリはコア/モデルで定義され、Log、Metric、Span およびその他のオブジェクトの作成を提供するため便利です。データ フローの柔軟な呼び出しを実現し、ビジネス シナリオの結合を改善するだけでなく、新しいデータ モデルのスケーラビリティも向上します。

ジェネレータパターン

パターン紹介

ビルダー モード(ビルダー モードとも呼ばれます) では、複雑なオブジェクトを段階的に作成でき、同じ作成コードでさまざまなタイプや形式のオブジェクトを生成できます。ジェネレーター パターンによって構築されるオブジェクトは大きくて複雑である必要があり、自動車の生産ラインなどの確立された製造プロセスに従って組み立てる必要があります。

Builder パターンは 4 つの役割で構成されます。

  • 製品: 複数の部分で構成され、それぞれが独自のビルド メソッドと表現を持つ複雑なオブジェクト。
  • ビルダー (抽象ビルダー): 各コンポーネントを構築するためのメソッドを含む、複雑なオブジェクトを構築するための抽象インターフェイスの定義を担当します。
  • ConcreteBuilder (コンクリート ジェネレーター): Builder インターフェイスを実装し、各コンポーネントの構築メソッドの実装を担当し、最終的にそれらを組み合わせて完全な複雑なオブジェクトを作成します。
  • Director (ディレクター): Builder オブジェクトの管理を担当し、Builder オブジェクトのメソッドを呼び出して複雑なオブジェクトを構築します。複雑なオブジェクトを直接作成するのではなく、Builder オブジェクトを通じて複雑なオブジェクトを構築します。

iLogtail の練習

iLogtail の Go パイプラインは、典型的なジェネレーター モード アプリケーション シナリオである複雑な生産ラインとみなすことができます。まず、パイプライン マネージャー (ディレクター) がパイプライン構築プロセスを複数のプラグイン構築ステップに分解し、パイプビルダーが各段階でプラグインの作成と初期化を完了し、最後にこれらのプラグインが完全なパイプライン オブジェクトに結合されます。 (製品)。

ジェネレーター モードの適用により、iLogtail プラグイン メカニズムの拡張性と保守性が大幅に向上し、ユーザーが実際のニーズに応じてさまざまな収集および処理シナリオを拡張するのに便利です。

プロトタイプパターン

パターン紹介

プロトタイプ パターンを使用すると、明示的なインスタンス化ではなく、既存のオブジェクトをコピーすることによって新しいオブジェクトを作成できます。

iLogtail の練習

プロトタイプ パターンは、類似したオブジェクトが多数作成されるシナリオでよく使用されます。iLogtail データ処理中に、プロトタイプ モードを使用して複数の同様の PipelineEvent オブジェクトを作成すると、データ処理の効率と保守性を効果的に向上させることができます。

要約する

作成パターンは一般に比較的単純で、その役割はインスタンス オブジェクトを生成することです。

  • シングルトン モード: クラスにインスタンスが 1 つだけあることを確認し、インスタンスへのグローバル アクセス ポイントを提供します。これは、一部のグローバル共有リソースを管理し、複数のインスタンス間の競合や競合を回避するのに適していますが、実装上の問題に注意を払う必要があります。
  • ファクトリ パターン: オブジェクトを作成するためのインターフェイスを定義しますが、どのクラスをインスタンス化するかはサブクラスに決定させます。より柔軟で、同様のプロパティを持つオブジェクトの作成に適しています。
  • ビルダー パターン: 複雑なオブジェクトの構築プロセスは、複数のステップに分割されて完了します。これは、コードの保守と拡張に便利な、いくつかの複雑なオブジェクトの作成に適しています。
  • プロトタイプ モード: オブジェクトをコピーする方法を使用して、複雑な作成プロセスを軽減します。

構造パターン

構造パターンの役割は、オブジェクト間の関係と相互作用を実現するためにオブジェクトを整理する方法を提供することです。

アダプターパターン

パターン紹介

アダプター パターンは、あるタイプのインターフェイスを別の必要なタイプのインターフェイスに変換し、互換性のないインターフェイスを持つオブジェクトが連携できるようにします。

iLogtail アプリケーション

iLogtail プロセスは 2 つの部分で構成されており、1 つは制御、ファイル収集、C++ 高速化処理、SLS 送信などの機能を提供する C++ で記述されたメインのバイナリ プロセスであり、もう 1 つはプラグイン部分 (libPluginBase.so) で記述されています。プラグインシステムで実現するGolangの処理能力の拡大と上流・下流のエコロジーサポートの充実。

iLogtail では、SLS 送信シナリオの主な実装ロジックは C++ Sender.cpp にあり、包括的な送信信頼性強化機能 (例外処理、再試行、バック プレッシャーなど) を提供します。Go Pipeline の SlsFlusher の場合も、収集・加工したデータを SLS に送信する必要があるため、Go プラグイン側で同じロジックを実装するとコードの冗長性が生じます。したがって、Go SlsFlusher の実装原理は、処理されたデータを C++ 部分に転送して、最終的なデータ送信を完了することです。ただし、言語間のシナリオでは非互換性の要因が存在するはずで、現時点では、libPluginAdaptor.so がアダプター層として機能し、Golang 送信インターフェースと C++ 送信インターフェース間の接続を実現します。

外観モード

パターン紹介

Facade パターンは、ライブラリ、フレームワーク、またはその他の複雑なクラスに単純なインターフェイスを提供することを目的としています。通常、ファサード クラスは一部のサブシステムの複雑な対話を保護し、シンプルなインターフェイスを提供することで、クライアントが実際に関係する機能に集中できるようにします。

iLogtail アプリケーション

K8s ログが SLS に収集されるシナリオでは、iLogtail は環境変数 ( aliyun_logs_{key} ) をサポートすることで、プロジェクト、ログストア、マシン グループ、収集構成などの SLS 関連リソースの作成を含む収集構成を自動的に完了します。全体的な操作が多く、構成の詳細、コンテナーのフィルター項目、操作シーケンス、障害など、多くの要素を考慮する必要があります。

iLogtail Env 収集シナリオでは、考慮する必要がある主要な構成項目はいくつかだけです。したがって、必要な関数をカプセル化し、コードの詳細を非表示にする外観クラスが実装されます。これにより、現在の呼び出し関係が簡素化されるだけでなく、将来のバックエンド API アップグレードの影響も最小限に抑えられます。プログラムを変更する必要があります。

ブリッジモード

パターン紹介

ブリッジ パターン (ブリッジ パターン) では、大規模なクラスまたは密接に関連した一連のクラスを、抽象化と実装の 2 つの独立した階層に分割して、開発中に個別に使用できるようにすることができます。この概念は比較的あいまいですが、別の理解方法としては、クラスには独立して変化する 2 つ (またはそれ以上) の次元があり、これら 2 つ (またはそれ以上) の次元は組み合わせによって独立して拡張できます。

iLogtail アプリケーション

iLogtail では、flusher_http を使用してさまざまなバックエンド システムに送信する場合、多くの場合、リクエストの署名と認証ヘッダーの追加をサポートする必要があり、リクエストの署名アルゴリズムはバックエンド プラットフォームによって異なる場合があります。より優れたスケーラビリティを実現するために、iLogtail は拡張メカニズムを提供します。これは、flusher_http プラグインの実装を特定の送信戦略の実装から分離し、それによって Authenticator、FlushInterceptor、および RequestInterceptors のスケーラビリティを実現します。

プロキシモード

パターン紹介

プロキシ モードは、プロキシ クラスを使用して具体的な実装クラスの実装の詳細を隠すことであり、通常は実際の実装の前後にロジックを追加するために使用されます。プロキシであるため、実際の実装をクライアントから隠す必要があり、プロキシはクライアントからのすべてのリクエストに対して責任を負います。

iLogtail アプリケーション

iLogtail の中心的なステップは、データがバックエンド サービスに正確に送信されることを保証することです。SLS にデータを送信するシナリオでは、最も基本的なことは、パッケージ化されたデータを送信するために SDK を呼び出すことです。プロセス全体は単純に見えますが、素晴らしい知恵が含まれています。バックエンド サービスは複雑で変更しやすいため、ネットワークの不安定性、バックエンド クォータの満杯、認証の失敗、時折のサービスの利用不能、フロー制御、プロセスの再起動などの不確実な要因が存在することがよくあります。各データ送信側が独立して処理し、SLS SDK を直接呼び出して送信すると、必然的に多数のコードが繰り返されることになり、コードの複雑さが増加します。したがって、iLogtail では、SDK の直接送信の信頼性を高めるために Sender プロキシ クラスを導入しています。データ送信者は、Sender::Instance()->Send を呼び出すだけで、データ送信が完了したと見なされ、残りの複雑なシーン処理はすべて Sender クラスに引き渡され、データが正常に送信されることが保証されます。バックエンドシステム。

要約する

プロキシ モードはメソッドを強化するために使用され、アダプタ モードは「鶏をアヒルに詰め込む」ようなインターフェイスの適応を実現し、ブリッジ モードは組み合わせによるシステムの分離を実現し、アピアランス モードはクライアントが気にしないようにします。インスタンス化プロセスでは、必要なメソッドを呼び出すだけです。

さらに、データを階層構造で記述するために使用される結合モードがあり、フライウェイト モードは、特定のシナリオで作成されたオブジェクトをキャッシュするためにパフォーマンスを向上させるために使用されます。

行動モデル

動作パターンは、オブジェクト間の効率的な通信と責任の委任に関与しており、さまざまなクラス間の相互作用に焦点を当て、責任を明確に分割して、コードをより明確にします。

オブザーバーパターン

パターン紹介

Observer パターンは、サブスクリプションとパブリケーションのメカニズムと同様に、オブジェクト間の 1 対多の依存関係を定義します。オブザーバブルの状態が変化すると、それに依存するすべてのオブジェクトに通知され、イベントが自動的に処理されます。オブザーバーモードにより柔軟なイベント処理が実現でき、オブジェクト間の関係が緩やかになるため、システムの拡張や保守に便利です。

iLogtail の練習

ファイル収集シナリオは、オブザーバー モードの典型的なアプリケーション シナリオと考えることができます。収集効率とクロスプラットフォームのサポートを考慮するために、iLogtail はポーリング (polling) とイベント (inotify) の共存モードを採用し、環境の包括性を実現します。

ログ読み取り動作は、イベントの形式で iLogtail の内部でトリガーされます。このうち、ポーリングと inotify は 2 つの独立したモジュールであり、それぞれによって生成された作成/変更/削除イベントをそれぞれポーリング イベント キューと Inotify イベント キューに格納し、最終的に統合されたイベント キューにマージされます。

  • ポーリング モジュールは、DirFilePolling と ModifyPolling の 2 つのスレッドで構成されます。DirFilePolling は、ユーザー構成に従ってフォルダーを定期的に走査し、ログ収集構成を満たすファイルを変更キャッシュに追加する役割を果たします。ModifyPolling は、ファイルのステータスを定期的にスキャンする役割を担います。キャッシュを変更し、以前のステータス (Dev、Inode、Modify Time、Size) を比較し、更新が見つかった場合は、変更イベントが生成されます。
  • inotify はイベント監視モードに属し、ユーザー設定に従って対応するディレクトリとサブディレクトリを監視し、監視ディレクトリが変更されると、カーネルは対応する通知イベントを生成します。

最後に、LogInput モジュールはイベント キューの消費を完了し、イベント ハンドラーに渡して作成/変更/削除などのイベントを処理し、実際のログ収集を実行します。

責任連鎖モデル

パターン紹介

Chain of Responsibility パターンを使用すると、ハンドラーのチェーンにリクエストを送信できます。リクエストを受信した後、各プロセッサはリクエストを処理するか、チェーン内の次のプロセッサにリクエストを渡すことができます。

責任の連鎖により、特定の動作がハンドラーと呼ばれる個別のオブジェクトに変換されます。長いプロセスでは、各ステップを 1 つのメソッドだけでクラスに抽出して操作を実行し、リクエストとそのデータをパラメータとしてメソッドに渡すことができます。

iLogtail の練習

iLogtail のデータ処理パイプラインは、非常に古典的な責任連鎖モデルです。現在のプラグイン システムの本体は、Input、Processor、Aggregator、Flusher で構成されており、Processor は、特定のフィールドが要件を満たしているかどうかの確認や追加、削除など、入力データをフィルタリングできる処理層です。そしてフィールドを変更します。各構成は同時に複数のプロセッサーを構成でき、シリアル構造を採用しています。つまり、前のプロセッサーの出力が次のプロセッサーの入力として使用され、最後のプロセッサーの出力がアグリゲーターに渡されます。

メモモード

パターン紹介

memento パターンを使用すると、オブジェクトの実装の詳細を公開することなくオブジェクトの内部状態をキャプチャし、この状態をオブジェクトの外部に保存できるため、後でオブジェクトを元の保存状態に復元できます。

メモ モードには主に次のコンポーネントがあります。

  • オリジネーター: 主に現時点の内部状態を記録し、どの状態がバックアップ範囲に属するかを定義し、メモデータの作成と復元を担当します。
  • Memento クラス (Memento) : イニシエーター オブジェクトの内部状態を保存し、必要なときに必要な内部状態をイニシエーターに提供する責任を負います。
  • 管理クラス (Caretaker) : メモの管理クラス。メモを保存および提供します。ただし、メモの内容にアクセスして変更することはできません。

iLogtail の練習

ログ収集シナリオで最も重要な機能は、ログが失われないようにすることです。iLogtail は、チェックポイント メカニズムを使用してファイル収集の状態をローカル ディスクに適時にバックアップし、極端なシナリオでもデータの信頼性を確保します。2 つの典型的なアプリケーション シナリオ:

  • 構成更新の収集/アップグレードの処理

構成が更新またはアップグレードされるとき、収集は中断され、収集コンテキストが再初期化される必要があります。iLogtail は、構成の更新またはプロセスのアップグレード中にログがローテーションされた場合でも、ログが失われないようにする必要があります。

解決策: 構成の更新/アップグレード プロセス中にログ データが失われないようにするため、iLogtail は構成をリロードする前、またはプロセスがアクティブに終了する前に、新しい構成が更新されるときに、現在収集されているすべてのステータスをローカル チェックポイント ファイルに保存します。 application/process 起動後、最後に保存されたチェックポイントがロードされ、チェックポイントを通じて以前の収集状態が復元されます。

  • プロセスのクラッシュやダウンタイムなどの異常事態

プロセスがクラッシュまたはダウンした場合、データを失わず、収集の繰り返しを可能な限り最小限に抑えるために、iLogtail はフォールト トレラント メカニズムを提供する必要があります。

解決策: プロセスのクラッシュやダウンタイムのため、終了する前にチェックポイントを記録する時間がないため、iLogtail は収集の進行状況を定期的にローカルにダンプします。通常のログ ファイルのステータスを復元することに加えて、リスクを最小限に抑えるためにローテーションされたログも検索します。ログ損失の可能性があります。

イテレータパターン

パターン紹介

Iterator パターンは、オブジェクトの内部の詳細を公開することなく、オブジェクトの個々の要素にアクセスする方法を提供します。

iLogtail の練習

Golang プラグインは、LevelDB を使用して、一部のコンテキスト リソースをバックアップし、イテレータ モードに基づいてデータを復元します。

// Iterator iterates over a DB's key/value pairs in key order.
type Iterator interface {
  CommonIterator
  
  // Key returns the key of the current key/value pair, or nil if done.
  // The caller should not modify the contents of the returned slice, and
  // its contents may change on the next call to any 'seeks method'.
  Key() []byte

  // Value returns the key of the current key/value pair, or nil if done.
  // The caller should not modify the contents of the returned slice, and
  // its contents may change on the next call to any 'seeks method'.
  Value() []byte
}

要約する

行動パターンは、オブジェクト間のコミュニケーションと相互作用の方法とパターンに焦点を当てます。

  • オブザーバー モード: 1 対多の依存関係を定義します。オブジェクトの状態が変化すると、そのすべての依存関係が通知され、自動的に更新されます。
  • 責任連鎖モード: リクエストの送信者と受信者を切り離し、オブジェクトの 1 つがリクエストを正常に処理するまで、複数のオブジェクトがリクエストを処理する機会を持ちます。
  • Memento パターン: オブジェクトの実装の詳細を公開せずに、オブジェクトの以前の状態を保存および復元できます。
  • イテレータ パターン: 内部構造を公開せずに、集合オブジェクト内の個々の要素にアクセスするための統一された方法を提供します。

参考:

私の作品における 23 のデザインパターンについて話します

デザインパターンを解説した記事(C++編)

これら 6 つの一般的なデザイン パターンを理解するにはどうすればよいでしょうか?

25,000語で23のデザインパターンを詳しく解説

C++ の一般的な設計パターン: https://refactoringguru.cn/desi

著者|イエモ

クリックして今すぐクラウド製品を無料で試し、クラウドでの実践的な取り組みを始めましょう!

元のリンク

この記事は Alibaba Cloud のオリジナルのコンテンツであり、許可なく複製することはできません。

中学3年生がWindows 12のWeb版deepinを書いた- IDEが正式デビュー、「真の独立研究開発」として知られる 同時に更新され、基礎となるNTアーキテクチャはElectron 「紅蒙の父」王成陸 基づく: 紅蒙 PC 版システムは来年開始され、文心は全社会に公開されます3.2.0 正式リリースグリーン言語 V1.0 正式リリース
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/yunqi/blog/10108259