パフォーマンスを向上させるためのチューニングのヒント¶
コンテンツ :
バッチ処理¶
サイジング¶
バッチ処理は、NGSIEvents
のセットを1つずつ処理するのではなく、一緒に処理するためにCygnus が実装するメカニズムです(NGSIEventは通常、Orionの通知からきます)。これらのセットまたは適切に記載されたバッチは、すべてのシンクが延びる基本クラスである NGSISink
によって構築されます。したがって、継承されたコードですでに作成されたバッチを持っていると、シンクはその中のデータの永続性を処理するだけで済みます。通常、バッチ全体内の情報は、単一の書き込み/挿入/upsert オペレーションを使用して同時に格納される大きなデータチャンクに集約されます。どうしてですか?
バッチメカニズムに関して重要なのは、書き込みの回数が大幅に減るため、シンクのパフォーマンスが大幅に向上することです。例を見てみましょう。100の通知、バッチ処理メカニズムなし、HDFSストレージを想定しましょう。100件の書き込みが必要であることは明らかです。
NGSIEvent
s /notifications ごとに1件。そして、ディスクへの書き込みは非常に遅いです。最良の場合、これらのすべての `NGSIEvent
s /notifications は同じエンティティに関するものです。つまり、その中のすべてのデータが同じ HDFS ファイルに保持されるため、1回の書き込みだけが必要です。
明らかに、すべての NGSIEvent
s /notifications が常に同じユニークなエンティティを考慮するとは限らず、多くのエンティティがバッチ内に関与している可能性があります。しかし、バッチ内で複数のサブバッチが作成されるため、最終的な HDFS ファイルごとに1つのサブバッチが作成されるため、問題はありません。最悪の場合、100のエンティティは100種類のエンティティ(100種類の HDFS 送り先)になりますが、これは通常のシナリオではありません。したがって、バッチあたり10〜15個のサブバッチが現実的であると仮定すると、イベントの100回の書き込みを10〜15個の書き込みしかないイベント・アプローチによって置き換えます。
それにもかかわらず、バッチを使用する場合、いくつかのリスクが発生します :
- 最初のリスクは最後のバッチが決して構築されないかもしれないということです。99個の
NGSIEvent
s/notifications が通知され、100番目のNGSIEvent
s/notifications が決して到着しない場合は、上記100サイズのバッチは決してシンクによって処理される準備ができていません。これは、バッチ・メカニズムが蓄積タイムアウトを追加して、新しいデータが到着しないときにシンクがバッチ・ビルディングの永遠の状態にとどまるのを防ぐ理由です。そのようなタイムアウトに達すると、バッチはそのまま維持されます。 - 2番目のリスクは、Cygnus がクラッシュしたり、蓄積中に停止したりすると、バッチ内のデータが失われる可能性があることです。バッチサイズまたはタイムアウトに達するまで、バッチ内のデータは永続化されず、データワークフロー内には存在しません。一般的な NGSI ソース である Orion Context Broker では、通知した後はおそらくはデータのコピーがもうないでしょう。これに関する研究中の問題があります。
デフォルトでは、すべてのシンクでは、設定されたバッチサイズは1で、バッチ蓄積タイムアウトは30秒です。これらはすべてのシンクがこれらの目的のために持つパラメータです :
<agent_name>.sinks.<sink_name>.batch_size = 1
<agent_name>.sinks.<sink_name>.batch_timeout = 30
それにもかかわらず、上で説明したように、パフォーマンス目的で少なくともバッチサイズを増やすことを強くお勧めします。どのような最適な値ですか?バッチのサイズは、イベントが得られているチャネルのトランザクションサイズに密接に関連しています(最初のものが第2のものよりも大きい)。また、予測されるサブバッチの数にも依存します。累積タイムアウトは、最終ストレージに新しいデータを出力する頻度に依存します。逆に、非常に大きなバッチサイズとタイムアウトは、Cygnus がクラッシュしたり、その間に停止したりすると、データの永続性に影響を与える可能性があります。
リトライ¶
バッチは永続化されないことがあります。これは、シンクが一時的に利用できないか、通信が失敗しているために時々発生する可能性があります。その場合、Cygnus はリトライ・メカニズムを実装しています。
永続化されていないリトライのバッチに関しては、2つのパラメータが使用されます。一方は、Cygnus がイベントを完全にドロップする前に行うリトライの回数を指定する Time-To-Live(TTL) が使用されます。0はリトライなしを意味し、-1は無限リトライを意味します。も一方は、再リトライ間隔のリストを構成することができます。そのようなリストは、最初の再リトライ間隔、次に2回目の再リトライ間隔などを定義します。TTL がリストの長さより大きい場合、最後の再リトライ間隔が必要な回数繰り返されます。
デフォルトでは、すべてのシンクに設定されたバッチTTLと再試行間隔がそれぞれ10ミリ秒と5000ミリ秒に設定されています。これらは、すべてのシンクがこれらの目的のために持つパラメータです :
<agent_name>.sinks.<sink_name>.batch_ttl = 10
<agent_name>.sinks.<sink_name>.batch_retry_intervals = 5000
パフォーマンスに関しては、一方では、リトライは、NGSIEvent
s/notification がエンキューされ、試行されたときに CPU を消費するため、Cygnus の処理速度を低下させます。しかし、最も重要なのは、永続性バックエンドが利用できないときです。新しい NGSIEvent
s/notification が再試行キューに追加されます。これは、無限の再試行(batch_ttl = -1
)を使用する場合に特に重要です。その場合、リトライ・キューのサイズは決して減少しません。
もう一方は、永続性バックエンドが回復するのにしばらく時間がかかると、非常に短いリトライ間隔でCygnusが無用に動作します。この効果は、無限の再試行(batch_ttl = -1
)を使用すると倍になります。
シンクの並列化¶
Cygnus が行う処理の大部分はシンクに位置し、これらの要素は適切に構成されていなければボトルネックになる可能性があります。
基本的な Cygnus の設定は、単一のシンクがそれらのイベントを消費する単一のチャネルに Flume イベントを書き込むソースについてです :
cygnus-ngsi.sources = mysource
cygnus-ngsi.sinks = mysink
cygnus-ngsi.channels = mychannel
cygnus-ngsi.sources.mysource.type = ...
cygnus-ngsi.sources.mysource.channels = mychannel
... other source configurations...
cygnus-ngsi.channels.mychannel.type = ...
... other channel configurations...
cygnus-ngsi.sinks.mysink.type = ...
cygnus-ngsi.sinks.mysink.channel = mychannel
... other sink configurations...
これは、並列に実行される複数のシンク構成に明確に移動できます。しかし、構成は1つではありませんが、多くはありません :
複数のシンク、シングルチャネル¶
同じ単一チャネルからイベントを消費するシンクを単純に追加することができます。この構成では、理論的にはシンク側で処理能力が向上しますが、通常、シンクで消費されるイベントが非常に高速である場合は重要な欠点があります。シンクは単一チャネルで競争する必要があります。このようにしてシンクを増やすだけでは、システムを単一のシンク構成よりも遅くすることができます。この設定は、シンクが単一のイベントを処理するために多くの時間を必要とする場合にのみ推奨され、チャネルにアクセスする際の衝突をほとんど防ぎません。
cygnus-ngsi.sources = mysource
cygnus-ngsi.sinks = mysink1 mysink2 mysink3 ...
cygnus-ngsi.channels = mychannel
cygnus-ngsi.sources.mysource.type = ...
cygnus-ngsi.sources.mysource.channels = mychannel
... other source configurations...
cygnus-ngsi.channels.mychannel.type = ...
... other channel configurations...
cygnus-ngsi.sinks.mysink1.type = ...
cygnus-ngsi.sinks.mysink1.channel = mychannel
... other sink configurations...
cygnus-ngsi.sinks.mysink2.type = ...
cygnus-ngsi.sinks.mysink2.channel = mychannel
... other sink configurations...
cygnus-ngsi.sinks.mysink3.type = ...
cygnus-ngsi.sinks.mysink3.channel = mychannel
... other sink configurations...
... other sinks configurations...
複数のシンク、複数のチャネル¶
上述の欠点は、各シンクごとにチャネルを構成し、単一チャネルの競合を避けることで解決できます。
ただし、複数のチャネルが同じストレージに使用されている場合、どのチャネルがイベントのコピーを受け取るかを決定する何らかの種類のディスパッチャが必要です。これは Flume Channel Selectors の目標で、Flumeイベントが挿入される適切なチャネルセットを選択するソフトウェアです。デフォルトのセレクタは、Replicating Chann
el Selector
です。つまり、ソースでFlume イベントが生成されるたびに、そのソースに接続されているすべてのチャネルでレプリケートされます。別のセレクタである Multiplexing Channel Selector
があります。このセレクタは、特定のマッチングのような条件が指定されたチャネルにイベントを配置します。それでも :
- Flume イベントは、構成された各ストレージごとに複製したいです。例えば、イベントは HDFS と CKAN の両方のストレージに永続化したいです
- しかし、ストレージ内では、Flume イベントをレプリケートされずに単一のチャネルに入れることが望まれます。たとえば、HDFS ストレージに関連付けられたすべてのチャネルの中で、イベントを単一のイベントに入れることができます
- また、ディスパッチ基準は一致ルールに基づいているのではなく、ラウンドロビンのような振る舞いに基づいています。例えば、HDFS ストレージに関連付けられた3つのチャネル(
ch1
、ch2
、ch3
)がある場合は、最初にch1
を選択し、次にch2
を選択し、次にch3
を選択してから、ch1
を選択します
利用可能な チャネル・セレクタ が私たちのニーズに合わないため、カスタム・セレクタが開発されました : RoundRobinChannelSelector
。このセレクタは、AbstractChannelSelector
を Replicating Channel Selector
および Multiplexing Channel Selector
として拡張します。
cygnus-ngsi.sources = mysource
cygnus-ngsi.sinks = mysink1 mysink2 mysink3
cygnus-ngsi.channels = mychannel1 mychannel2 mychannel3
cygnus-ngsi.sources.mysource.type = ...
cygnus-ngsi.sources.mysource.channels = mychannel1 mychannel2 mychannel3 ...
cygnus-ngsi.sources.mysource.selector.type = com.telefonica.iot.cygnus.channelselectors.RoundRobinChannelSelector
cygnus-ngsi.sources.mysource.selector.storages = N
cygnus-ngsi.sources.mysource.selector.storages.storage1 = <subset_of_cygnusagent.sources.mysource.channels>
...
cygnus-ngsi.sources.mysource.selector.storages.storageN = <subset_of_cygnusagent.sources.mysource.channels>
... other source configurations...
cygnus-ngsi.channels.mychannel1.type = ...
... other channel configurations...
cygnus-ngsi.channels.mychannel2.type = ...
... other channel configurations...
cygnus-ngsi.channels.mychannel3.type = ...
... other channel configurations...
cygnus-ngsi.sinks.mysink1.type = ...
cygnus-ngsi.sinks.mysink1.channel = mychannel1
... other sink configurations...
cygnus-ngsi.sinks.mysink2.type = ...
cygnus-ngsi.sinks.mysink2.channel = mychannel2
... other sink configurations...
cygnus-ngsi.sinks.mysink3.type = ...
cygnus-ngsi.sinks.mysink3.channel = mychannel3
... other sink configurations...
... other sinks configurations...
基本的に、カスタム・チャネル・セレクタのタイプは、ストレージごとのチャネルのマッピングと共に設定する必要があります。このマッピングは、次の形式で構成されます :
- 異なるストレージの合計数。 例えば、MySQL ストレージ、CKAN ストレージ、HDFS ストレージがある場合は、
cygnus-ngsi.sources.mysource.selector.storages = 3
となります。これは、同じタイプの異なるストレージに適用されます。例えば MySQLストレージと2つの異なる HDFS ストレージ(つまり異なる HDFS エンドポイント)がある場合は、cygnus-ngsi.sources.mysource.selector.storages = 3
も同様です - 各ストレージに関連付けられたチャネルのサブセット。 すべてのサブセットの和集合は、ソース用に構成されたすべてのチャネルと等しくなければなりません。例えば、もし
cygnus-ngsi.sources.mysource.channels = ch1 ch2 ch3 ch4 ch5 ch6
であり、ch1
がMySQLストレージに関連付けられていれば、ch2
およびch3
が CKAN ストレージに関連付けられ、ch4
、ch5
およびch6
が HDFS ストレージに関連付けられている場合、cygnus-ngsi.sources.mysource.selector.storages.storage1 = ch1
,cygnus-ngsi.sources.mysource.selector.storages.storage2 = ch2,ch3
とcygnus-ngsi.sources.mysource.selector.storages.storage3 = ch4,ch5,ch6
に関連付けられます
なぜ LoadBalancingSinkProcessor
が適切でないのか¶
この Flume Sink Processor は、ロードバランシングがシーケンシャルな方法で行われるため、私たちの並列化には適していません。つまり、ロードバランサのラウンドロビンのような構成でもランダムな方法でも、シンクは1つずつ使用され、同時には使用されません。
チャネルの考慮事項¶
チャネルタイプ¶
Cygnus (一般的には、Flume ベースのアプリケーション)のチャネルを設計する際に最も重要なことは、速度と信頼性のトレードオフです。これは特にチャネルに当てはまります。
一方は、MemoryChannel
はメモリに直接実装されているため非常に高速なチャネルですが、たとえば Cygnus が何らかの理由でクラッシュし、サードパーティシステム(Monit と言いましょう)によって復元された場合 : その場合、Flume イベントは、クラッシュが失われる前にメモリベースのチャネルに入れられます。もう一方は、FileChannel
および JDBCChannel
は、それぞれ OS ファイルまたは RDBM テーブルに関してデータを永続的にサポートするため、非常に信頼性があります。 それにもかかわらず、I/Oはメモリに対してではなく HDD に対して行われるため、MemoryChannel
よりも遅くなります。
チャネル容量¶
チャネル容量が多数設定されている場合のパフォーマンスの低下を示す経験的テストはありませんが、1億回の Flume イベントを考えてみましょう。MemoryChannel
は、連鎖 FIFO キューとして設計されており、永続チャネルは、実際のデータへのポインタのリストしか管理しません。これは、反復するのが難しいはずです。
そのような大容量は、Flume ソースが Flume シンクよりも速い(その場合でも、遅かれ早かれ、チャンネルが満杯になる)、またはシンク内で多くの処理のリトライが予想される場合にのみ必要となります。次のセクションを参照してください。
適切な容量を計算するには、次のパラメータを考慮してください :
- 単位時間あたりのソースによってチャネルに入れられるイベントの量です。1分とします
- 単位時間当たりのシンクによってチャネルから取得されるイベントの量
- 単位時間当たりに処理できなかったイベントの量の見積もり。したがって、チャネルに再注入されます。次のセクションを参照してください。
名前マッピング¶
名前マッピング機能は、元の通知された FIWARE service、FIWARE service path、エンティティ ID とタイプ、および属性名とタイプを変更するための強力なツールです。この変更の副次効果として、名前マッピングは、たとえば2つ以上の元のサービスパスの共通の代替 FIWARE service path を設定するなどして、データのルーティングに使用できますこれらのサービスパスに関するすべてのデータは、同じ CKAN パッケージに格納されます。
あなたが考えているように、名前マッピングの使用は、Json 形式で書かれたマッピングのリストをチェックした後に代替設定が得られるので、Cygnus を遅くします。このような Json がメモリにロードされ、正規表現がパターンにコンパイルされているにもかかわらず、NGSIEvent
/notification が Cygnus に送信され、照合の条件がチェックされるたびに反復されなければなりません。
それにもかかわらず、スマートな方法で名前マッピングを書くことができます :
- 最も可能性の高いマッピングを最初に配置します。チェックはシーケンシャルなので、特定のイベントに対して適切なマッピングが見つかるとすぐに、別の
NGSIEvent
/notification をチェックすることができます。したがって、これらのマッピングをリストの最初の場所にあるNGSIEvent
/notification の大部分に適用すると、パフォーマンスが向上します。次に、マッピングを2番目の主要なNGSIEvent
/notification のセットに適用するなどします - 最も単純なマッピングのセットは、コンテキストエンティティと属性、およびそれらが属する FIWARE service と FIWARE service path の命名と型付けの最も単純な方法に由来します。簡単にグループ化できる名前を使用してみてください。numeric rooms と not numeric rooms は、
room\.(\d*)
とroom\.(\D*)
のような2つの正規表現だけを使用して簡単にモデル化することができますが、それらの名前を付けるより無秩序な方法は、はるかに異なるより複雑なマッピングに確実につながります
グループ化ルール¶
重要な注意 : リリース1.6.0から、この機能は、名前マッピングのために推奨されなくなりました。詳細はこちらをご覧ください。
グループ化ルールの機能は、データをルーティングするための強力なツールです。つまり、代替 IWARE service path とエンティティを設定し、最終的に HDFS ファイル、コンテキストデータ用の MySQL/PostgreSQL/DynamoDB/Carto テーブル、CKAN リソース、Kafka キュー、または MongoDB コレクションを決定します。逆に、デフォルトの宛先が使用されます。
あなたが想定しているように、グループ化ルールの使用は、代替の FIWARE service path とエンティティが規則のリストを順番にチェックして正規表現マッチを見つけようとした後に設定されるため、Cygnus を遅くします。ここでは、正規表現のマッチングが遅いことを覚えておくと、必要なだけ多くのグループ化ルールを設定することができます。
それにもかかわらず、グループ化ルールをスマートに書くことができます :
- 最も可能性の高いルールを最初に置きます。検査が順次であるため、特定のイベントに対して適切なルールが見つかるとすぐに、より早く別のイベントをチェックすることができます。したがって、リストの最初の場所でイベントの大半に適用されるルールを適用すると、パフォーマンスが向上します。その後、ルールを第2の主要なイベントのセットに適用するなどします
- 最も単純に一致する規則セットは、コンテキストエンティティ、そのタイプ、またはそれらが属するファイルサービスの名前を付ける最も単純な方法に由来します。簡単にグループ化できる名前を使用してください。例えば、numeric rooms と not numeric rooms は、
room\.(\d*)
とroom\.(\D*)
のような2つの正規表現だけを使って簡単にモデル化することができますが、それらを命名するより無秩序な方法は、はるかに異なるより複雑な規則に確実に導くでしょう
ログの書き込み¶
ディスクへの書き込みが含まれているI/O オペレーションのように、ログを書き込むことは大幅に遅くなります。必要な場合を除いて、巨大な数値を書くのは避けてください。つまり、あなたが Cygnus をデバッグしているので、少なくとも INFO レベルで cygnus を実行してみてください(多くのログがまだそのレベルで書かれているにもかかわらず)。ベストはERROR
レベルでの実行です。OFF
レベルを使用すると、ログは完全に無効になります。
Cygnuxの ロギング・レベルは、/usr/cygnus/conf/log4j.properties
に設定されてます。INFOデフォルトで設定されています :
flume.root.logger=INFO,LOGFILE