非HTTP負荷テスト。 パート1 JMeter

モノリスでは、メソッド呼び出しは同じアプリケーション内で行われました-現在、マイクロサービスアーキテクチャでは、すべての相互作用はネットワークを介して実行されます。 したがって、アプリケーション間の情報交換レートは、使用される交換プロトコルに関係なく低下します。







この記事では、 JMeterGatlingパート2 )などのツールを使用した例として、 Apache Thriftを使用した非HTTPプロトコルの負荷テストのためのコードの作成方法を示します。 50K RPSに対応するマイクロサービスをテストします。 1台のロードマシンで、このツイートに記載されているパフォーマンスを達成しようとします。









Thriftを基本としていますが、これは特別な役割を果たしておらず、 gRPCAvro 、およびその他のプロトコルをテストできる小さな調整があります。 この記事のアプローチは一般的なものであり、クライアントを置き換える必要があるだけです。







明確化

正式には、説明されているプロトコルはRPCフレームワークおよび/またはデータシリアル化システムですが、開発者自身もプロトコルという言葉を使用してい ます







JMeterとGatlingを選ぶ理由



1つの理由は、オープンソースの起源です。 コードを調べて、ツールに何がどのように実装されているかを理解してください。 そして、支払う必要はありません。







GitHubのオープンスペースには、特定のプロトコル用に強化されたツールがあります。 たとえば、Thriftの場合、Twitter開発者のIagoやPinterestの男性のベンダーです。 しかし、このようなフレームワークを使用すると、エントリのしきい値とバス係数が劇的に増加します。 対照的に、ガトリングと特にJMeterは広く普及しており、大きなコミュニティがあり、いつでもアドバイスを求めたり、問題の説明を見つけることができます。







まあ、私たちにとって、理解できない状況でコードを書くファンとして、両方のツールは素晴らしいです。







次の質問:2つのツールのいずれかを選択してみませんか? 一見したところ、それらの間には同等性があり、すべてを詳細に理解する必要があります。 さらに、アプローチ-Threads vs Actorsを比較することは興味深いです。







Jmeter



JMeterはGUI指向のロード手段ですが、そのためのコードを書くことができます。 このアプローチにより、可読性が向上し、データをVCSに適切な形式で保存し、目を痛めることなくコードレビューを実施できます。 さらに、任意のプロトコルのクライアントを作成するには、コードが十分であるだけでなく、必要な条件もあります。 最後に、このコードを使用すると、特定のツールについて説明することなく、開発を支援することが容易になり、それによって再びバスファクターが低下します。







カスタムプロトコルをロードするためにJMeterが提供するものを見てみましょう。









サンプラーの性能比較



どのサンプラーを使用するかを理解するために、比較分析を実施します。 テストのために、ループ内の行を連結し、VALUEをsmall(10)およびlarge(100)に設定する簡単なスクリプトを作成します。







import java.security.SecureRandom; for (int i = 0; i < VALUE; i++) { new StringBuilder().append( new SecureRandom().nextInt()); }
      
      





すべてのテストはJMeter 3.3で実施されました。







画像







JSR223の方が遅いことがわかります。つまり、クラッシュします。 JavaとJunitのパフォーマンスは似ていますが、どちらがより便利かを理解するには、両方を分解する必要があります。







Javaリクエストサンプラー



画像







Java Samplerには、負荷のテストの選択とテストに渡すためのパラメーターの2つの設定しかありません。 コードで一連のデフォルトパラメータを指定できます。これらはJMeter GUIに表示されます。 ただし、GUIから新しいパラメーターを直接追加し、テスト計画を保存すると、追加したパラメーターがリセットされることに注意してください。







Java Samplerテストは、JMeterライブラリの標準的なAbstractJavaSamplerClientを拡張します(Gradleなどの便利なビルドツールと接続します)。 実際、テストクラスは、テスト自体、事前条件と事後条件、およびデフォルトパラメータで構成できます。







 public class ThriftJavaSampler extends AbstractJavaSamplerClient { @Override public SampleResult runTest(JavaSamplerContext javaSamplerContext) { SampleResult sampleResult = new SampleResult(); sampleResult.sampleStart(); boolean result = Utility.getScenario(); sampleResult.sampleEnd(); sampleResult.setSuccessful(result); return sampleResult; } }
      
      





テストメソッドは、パラメーターを取得するJMeterコンテキストを取得し、SampleResultを返します。SampleResultには、テスト実行の構成と結果の評価に役立つ一連のメソッドが含まれています。 私たちの目的にとって、指定された3つのメソッドは重要です。リクエストの開始時間と終了時間、および結果です。







Junitリクエストサンプラー



画像







Junit Samplerにはロード用のテスト選択もあり、ここで1つのクラスに複数のメソッドを記述できます。 デフォルトのパラメーターは、ユーザー定義変数要素を介してコードにスローされます。 他のすべての設定は説明から明らかです。事前条件と事後条件を呼び出さず、出力にアサートエラーとランタイムエラーを追加します。 パフォーマンスを大幅に低下させるため、新しいリクエストごとにテストインスタンスを作成することは含めないでください。







Junit Request Samplerは、通常のJunitテストに似ていますが、動作が少し異なります。 JMeterは@BeforeClassと@AfterClassを呼び出すことはありません。そのため、個別のテストを使用してグローバル前提条件を構成する必要があります。 また、 BeforeおよびAfterのコードは、テストランタイムでは考慮されないことに注意してください。







 @BeforeClass public static void setUpClass() {assert false;} @Before public void setUp() {} @Test public void test() {} @After public void cleanUp() {} @AfterClass public static void cleanUpTest() {assert false;}
      
      





JMeter開発者自身は、GUIモードはデバッグにのみ使用すべきだと言っています。 しかし、ここでは静的とシングルトンに注意する必要があります。 たとえば、テストを再起動した後、シングルトン内で宣言されているものはすべて初期化されません。 同時に、シングルトンを使用せずに、各テストの前にすべてのオブジェクトが再初期化され、パフォーマンスに悪影響を及ぼします。 静的変数は、テストの実行後にその値を永久に記憶し、GUIから再定義されても変更されません。







両方のサンプラーを比較して、最終的には簡単で修正しやすいJunit Request Samplerに決めました。







Thriftクライアントの作成



クライアントを作成するときは、Thriftプロトコルのさまざまな設定を考慮する必要があります。 主なルール:クライアントとサーバーは同じトランスポートとプロトコルで動作し、アーティファクトのバージョンが1つ必要です。







画像







各テストでクライアントを作成する時間を無駄にせず、負荷を生成するマシンのポートを使い果たさないように、すぐにクライアントプールを書き込みます。 プールについては、クライアントの数を指定できます。これは、必要な数の接続を維持します。使用前にプールからクライアントを取り出し、使用後に戻るだけです。







 asyncClientPool = new ThriftClientPool<>(() -> new PaymentsCreate.AsyncClient( (TProtocolFactory) tTransport -> new TMultiplexedProtocol(new TBinaryProtocol(tTransport), SERVICE), new TAsyncClientManager(), new TNonblockingSocket(host, port, TIMEOUT)), PoolConfig.get());
      
      





これが、プールとクライアントの作成方法です。 主なことは、この例では、プールは実装について何も知らず、作成時に構成されます。 これは、GenericObjectPoolで記述された最も基本的なプールです。これに基づいて、開発者はロギングと単一のプロトコル/トランスポートで自己調整プールを作成しました。







コードを記述し、jarファイルに入れて、JMeterの横に置きます。







 $JMETER_DIR/lib/junit $JMETER_DIR/lib/ext $JMETER_DIR/lib
      
      





サードパーティライブラリとその一意性を忘れないでください。 それらがないと、テストは使用可能なもののリストに表示されないか、ロードの開始時にバージョンの競合が発生しない場合があります。







jmxのないJMeter



JMeterのコードの記述に関する記事は、jmxファイルの何百ものXML行の面倒なテスト計画を取り除く方法を議論しない限り、不完全です。







XML jmxシート、4MB

画像







JMeterライブラリを使用してアプリケーションを記述し、主要なポイントを順番に説明します。

アプリケーションを開始する前に、JMeterの設定が存在する場所を指定する必要があります。 同時に、JMeterのローカルインストールはオプションです。







 final String JMETER_HOME = Utility.getJMeterHome(); JMeterUtils.loadJMeterProperties(JMETER_HOME + "jmeter.properties"); JMeterUtils.initLogging(); JMeterUtils.setLocale(Locale.ENGLISH); JMeterUtils.setJMeterHome(JMETER_HOME);
      
      





テスト計画に名前を付けて、プロトコルを示しましょう。







 TestPlan testPlan = new TestPlan("Thrift test"); TestElement sampler = Utility.getSampler();
      
      





LoopControllerとThreadGroupは、ロードジェネレーター(ロードする方法と量)を担当します。 ここではすべてが標準です。







 LoopController controller = new LoopController(); controller.setLoops(10); controller.setContinueForever(false); ThreadGroup threadGroup = new ThreadGroup(); threadGroup.setNumThreads(10); threadGroup.setRampUp(0); threadGroup.setDuration(30); threadGroup.setSamplerController(controller);
      
      





ロード中に結果を圧縮形式で表示し(サマライザーのおかげ)、さらに処理するために結果を保存できます(resultCollectorがこれを担当します)。







 Summariser summariser = new Summariser(); SampleSaveConfiguration saveConfiguration = new SampleSaveConfiguration(true); ResultCollector resultCollector = new ResultCollector(summariser); resultCollector.setFilename(JMETER_HOME + "report.jtl"); resultCollector.setSuccessOnlyLogging(true); resultCollector.setSaveConfig(saveConfiguration);
      
      





すべての要素は、特別なJMeterツリーで結合されます。 GUI modでそれを行う方法のように見えます:







 HashTree config = new HashTree(); config.add(testPlan) .add(threadGroup) .add(sampler) .add(resultCollector);
      
      





ロードを構成して実行します。







 StandardJMeterEngine jMeterEngine = new StandardJMeterEngine(); jMeterEngine.configure(config); jMeterEngine.runTest();
      
      





XMLについて忘れることができるすべて。 やった!







究極の負荷



発電機が1台の負荷機械からどれだけ生産できるかを見てみましょう。 当然、負荷テスト用のすべてのシステム設定が行われました。ネットワーク設定を既に考慮し、オープン記述子の制限を増やしました。 実行後、約30K RPSを実行できるロードスケジュールはあまり均一ではありません。







画像







画像







問題は当然発生しますが、これはクライアント側またはサーバー側の制限ですか? 別のJMeterをクラスターに一緒に配置し、サービスがターゲット5万RPSを発行できることを確認しました。







PS



次のパートでは、ガトリングを使用して非HTTP負荷を分析し、パフォーマンスを10倍に高める方法を説明します。








All Articles