DUICUO

Meituanが負荷テストツールをゼロから構築した方法

[[161976]]

Meituan の社内 RPC サービスは主に Thrift 上に構築されています。日々のサービス開発においては、潜在的な問題を特定するためにストレステスト(以下、負荷テスト)を実施する必要があります。一般的な方法には以下のものがあります。

◆PythonやRubyなどのスクリプト言語を使用してオンラインログを読み取ってリクエストを構築し、マルチスレッドを使用してユーザーリクエストをシミュレートして負荷テストを行います。

◆ 負荷テストにはオープンソース ツールを使用します。

ただし、使用する方法に関係なく、負荷テストは非常に時間がかかり、面倒なプロセスであり、主な問題点は次のとおりです。

◆ ログを解析してリクエストを再構築するには、大量のコードを記述する必要があります。複雑なリクエストの場合、解析によってエラーが発生しやすくなります。

◆スクリプトまたはツールの実行時環境を設定する必要がありますが、これは通常、時間のかかるプロセスです。

◆圧力抑制のための統一された手法がないため、抑制結果と指標は非常に混乱しており、端末出力の形で表示されるものもあり、直感的ではありません。

◆ 環境やコードの問題により、アプリケーションのストレス テストをチーム メンバー間で共有することが困難です。

上記の問題に対処するには、シンプルで使いやすい負荷テスト ツールを提供することが不可欠です。

車輪の再発明は必要でしょうか?

負荷テストツールを構築する前に、既存のオープンソースツールをいくつか調査しました。現在利用可能な主な負荷テストツールは次のとおりです。

1. Jメーター

JMeterは比較的定着している負荷テストツールで、主にHTTPサービスのストレステストに使用されています。しかし、このツールはMeituanの社内負荷テストのニーズを以下の点で満たしていません。

◆Thrift ストレス テストはデフォルトではサポートされていません。

◆ローカルインストールが必要で、構成が複雑です。

◆ ユーザーフレンドリーではない。

2. Twitter/iago

iagoは、Twitterが開発したオープンソースの負荷テストツールで、HTTPやThriftなどのサービスの負荷テストをサポートしています。主な問題点は次のとおりです。

◆負荷テストアプリケーションごとにプロジェクトを作成する必要があります。

◆ストレステストの結果は直感的ではありません。

◆トラフィックの再生はローカルファイルに依存します。

◆このプロジェクトは Scala の古いバージョンに依存しているため、セットアップが不便です。

◆ 関連文書が比較的少ない。

さらに、Gatling、Grinder、Locust などの一般的な負荷テスト ツールも検討されましたが、適用可能なシナリオが Meituan のニーズと完全には一致しなかったため、すべて除外されました。

結論として、負荷テスト ツールの現状を考慮すると、シンプルで使いやすい負荷テスト ツールを構築することが依然として非常に重要です。

ターゲット

前述の問題点に対処するために、新しい負荷テスト ツールは主に次の機能を提供します。

◆ オンライントラフィックのコピー。

◆シンプルで使いやすい操作インターフェース(負荷テストの時間は1時間以内に制御する必要があります)。

◆ 明確なチャートは、ストレス テスト アプリケーションのさまざまな指標を反映できます。

◆ Thrift や HTTP などのサービスの負荷テスト要件を満たします。

構築方法

抽象的な

目標は明確ですが、それをどのように達成するのでしょうか?最初のステップは、ストレステストのプロセスを抽象化することです。

典型的な負荷テストのプロセスを図に示します。まず、`init`メソッドで、データベースへの接続やクライアントの作成などの初期化作業が行われます。次に、`run`メソッドで負荷テストリクエストが発行されます。サービスに十分な負荷をかけるために、通常はマルチスレッド同時アクセスが使用され、各リクエストの開始時間と終了時間が記録されます。この2つの時間を単純に差し引くことで、各リクエストの応答時間が得られます。この結果を使用して、TP90、平均応答時間、最大応答時間などのメトリックを計算できます。負荷テストが完了すると、`destroy`メソッドを使用してリソースの回収が行われます。

上記のプロセスはインターフェースで表現できます。ThriftサービスでもHTTPサービスでも、負荷テストの本質は同じです。つまり、これら3つのメソッドの異なる実装です。負荷テストツールの柔軟性と汎用性を考慮すると、負荷テストツールはこのインターフェース実装を負荷テストチームに委任し、マルチスレッド負荷テストや負荷テスト結果の集計といった時間のかかるタスクの実装に注力することができます。

  1. インターフェースランナー{
  2. def init(Test app) // 負荷テストを初期化する
  3. def run(Test app, String log) // リクエストの構築を容易にするために、各負荷テストリクエストのログを渡します。
  4. def destroy(Test app) // 負荷テストが完了したらリソースを再利用します
  5. }

コピートラフィック

Thriftサービスの負荷分散における課題の一つは、実際のオンライントラフィックをコピーして負荷分散リクエストを構築する簡単な方法を見つけることです。一部の大規模なThriftサービスは非常に複雑なデータ構造を持ち、負荷分散スクリプトを作成する際にログ解析のための膨大なコードを必要とし、エラーが発生しやすい傾向があります。そのため、トラフィックをコピーするためのシンプルで使いやすい方法を提供することが不可欠です。

ここで紹介する負荷テストツールは、トラフィックをコピーするための VCR (Video Recorder) というツールを提供しています。VCR はオンラインリクエストをシリアル化し、Redis に書き込むことができます。

ユーザーが特定のリクエストを視覚的に確認したいというニーズと使いやすさを考慮し、最終的にJSON形式がシリアル化およびデシリアル化プロトコルとして選択されました。さらに、本番環境への導入が必須であるため、オンラインサービスへの影響を最小限に抑えるため、トラフィックのコピーにはシングルスレッドの非同期書き込みアプローチを採用しました。

集計データ

アプリケーションストレステストが完了したら、結果を評価するためにいくつかの指標が必要です。一般的な指標には以下が含まれます。

◆最大応答時間

◆平均応答時間

◆QPS

◆TP90

◆TP50

負荷テスト ツールは、InfluxDB を使用してデータ集約を実行します。

TP90 を例にとると、1 行のクエリだけで要件を達成できます。

  1. test_series GROUPからPERCENTILE(response_time, 90)を選択 による 時間(10秒)

建築

全体として、抑制プロセス全体は次のようになります。

練習する

コピートラフィック

Meituan の内部サービスのほとんどは Java を使用して構築されており、VCR は Maven パッケージとしてユーザーに提供されます。

ユーザーにとっては、トラフィックをコピーするのに 2 行のコードしか必要ありません。

オンライン サービスへの影響を避けるため、通常はトラフィックのコピーに 1 台のマシンが選択されます。

  1. パブリッククラス TestAppRPC は TestApp.Iface を実装します {
  2.  
  3. private Vcr _vcr = new Vcr( "testapp" ); //トラフィックをコピーするためのキーを指定する 
  4.  
  5. @オーバーライド
  6. パブリックTestResponse echo(TestRequest req) は TException をスローします {
  7. _vcr.copy(req); // コピー操作
  8. 長い開始 = System.currentTimeMillis();
  9. TestResponse レスポンス = 新しい TestResponse();
  10. 応答を返します
  11. }
  12. }

トラフィックのコピーが完了すると、ユーザーは Web インターフェイスを通じてログ収集ステータスと個々のログエントリの詳細を表示できます。

負荷テストロジックの実装

負荷テストツールはGroovyで記述されています。各アプリケーションに対して、ランナーインターフェースを実装するだけで負荷テストを実行できます。

  1. インターフェースランナー{
  2. def init(テストアプリ)
  3. def run(テストアプリ、文字列ログ)
  4. def destroy(テストアプリ)
  5. }

Thrift サービスを例に挙げてみましょう。

  1. クラス TestServiceRunner は Runner を実装します {
  2.  
  3. RPCService.Client _client
  4. TTransport _トランスポート;
  5.  
  6. @オーバーライド
  7. def init(テストアプリ) {
  8. def conf = app.config // アプリケーション構成を読み取る
  9. _transport = 新しいTFramedTransport(新しいTSocket(conf.get( "thrift_service_host" )を文字列として、conf.get( "thrift_service_port" )を文字列として 整数))
  10. TProtocol プロトコル = 新しい TBinaryProtocol(_transport)
  11. _client = 新しい RPCService.Client(プロトコル)
  12. _transport.open ()
  13. }
  14.  
  15. @オーバーライド
  16. def run(テストアプリ、文字列ログ) {
  17. TestRequest req = Vcr.deSerialize(log, TestRequest.class) // コピートラフィックをデシリアライズする
  18. _client.echo(req) // リクエストを送信
  19. }
  20.  
  21. @オーバーライド
  22. def destroy(テストアプリ) {
  23. _transport.close () // サービスをシャットダウンする
  24. }
  25. }

アプリケーションを作成する

上記のインターフェースを実装したら、アプリケーションを抑制できます。

ユーザーは Web インターフェースを通じてアプリケーションを作成でき、必要な構成とは別に、アプリケーションに応じて柔軟に構成できます。

パフォーマンス指標

ユーザーは直感的なグラフを通じてアプリケーションのさまざまなパフォーマンス メトリックを表示できます。

結論

リリース以来、この負荷テストツールは20以上のアプリケーションに統合され、数百件の負荷テストを完了しました。アプリケーションの統合にはわずか15~30分しかかかりません。これにより、Meituanのサービスの安定性が確保され、開発者の時間を節約し、これまで面倒で長時間を要していた負荷テストプロセスから解放されます。