シンプルで高速なアプリケーションストレステストフレームワーク

[ 英語版 ]



最近、高負荷用のWebアプリケーションがますます作成されていますが、フレームワークを使用して、ストレスを柔軟にテストし、独自のロジックを追加できるため、それほど多くはありません。

もちろん、さまざまなものがあります(投稿の最後の投票を参照)が、誰かがCookieをサポートしていない、誰かが弱い負荷を与えている、誰かが非常に重い、そして非常に類似したリクエストに最も適している、すなわち 独自のロジックを使用して、可能な限り高速に(そして、理想的にはJavaで終了するのが理想的です)、各ロジックを使用して各リクエストを動的に生成します-そのようなものは見つかりませんでした。



したがって、これらはこの場合は3〜5のクラシックのみであるため、自分でスケッチすることにしました。 基本要件:速度と動的なクエリ生成。 同時に、速度は数千RPSだけでなく、理想的には-ストレスがネットワーク帯域幅のみに依存し、任意の空きマシンで機能する場合です。



エンジン




要件が明確になったので、今度はそれがすべて動作するかを決定する必要があります。 使用するhttp / tcpクライアント。 もちろん、マシンのパワーとjvmのコンテキスト切り替えの速度に応じて数千rpsに達するため、古い接続ごとのスレッドモデルを使用したくありません。 T.O. apache-http-clientなどは一掃されます。 ここでは、いわゆる NIO上に構築されたノンブロッキングネットワーククライアント。



幸いなことに、このニッチのJavaの世界では、非常に普遍的で低レベルである標準的な事実上のオープンソースNettyが長い間存在しており、tcpおよびudpで作業できます。



建築




送信者を作成するには、Nettyの観点からChannelUpstreamHandlerハンドラーが必要で、 そこからリクエストが送信されます。



次に、高性能タイマーを選択して、1秒あたりの最大リクエスト数(rps)を送信する必要があります。 ここでは、標準のScheduledExecutorServiceを使用できます。これは基本的にこれに対処しますが、タスクを追加するときのオーバーヘッドが少ないため、弱いマシンではHashedWheelTimer (Nettyに付属)を使用する方が適切です。 強力なマシンでは、実質的に違いはありません。



最後に、このOSの接続の制限または現在の総負荷がわからないときに、任意のマシンから最大rpsを圧縮するために、試行錯誤の方法を使用することが最も信頼できます。新しい接続を作成するときにエラーが始まる接続の数。 実験により、rpsの最大数は通常この数値よりわずかに少ないことが示されています。

つまり この値を初期rps値として使用し、エラーが繰り返される場合は10〜20%削減します。



実装


リクエスト生成




動的なクエリ生成をサポートするために、次のリクエストのコンテンツを受信するためにストレスが引き起こす唯一のメソッドを持つインターフェースを作成します。

public interface RequestSource { /** * @return request contents */ ChannelBuffer next(); }
      
      







ChannelBufferは、Nettyのバイトストリームの抽象化です。 ここでは、リクエストのコンテンツ全体がバイトストリームとして返される必要があります。 httpおよびその他のテキストプロトコルの場合、これはクエリ文字列(テキスト)の単なるバイト表現です。

また、httpの場合リクエストの最後に改行文字を2つ入れる必要があります (\ n \ n)。これはNettyのリクエストの終了のサインです(それ以外の場合はリクエストを送信しません)。



派遣


Nettyにリクエストを送信するには、まずリモートサーバーに明示的に接続する必要があります。そのため、クライアントの起動時に、現在のrpsに応じた頻度で定期的な接続を開始します。

 scheduler.startAtFixedRate(new Runnable() { @Overrid public void run() { try { ChannelFuture future = bootstrap.connect(addr); connected.incrementAndGet(); } catch (ChannelException e) { if (e.getCause() instanceof SocketException) { processLimitErrors(); } ... }, rpsRate);
      
      







接続に成功すると、すぐにリクエスト自体を送信するため、NettyハンドラはSimpleChannelUpstreamHandlerから便利に継承します。SimpleChannelUpstreamHandlerには特別なメソッドがあります。 ただし、注意点が1つあります。新しい接続は、いわゆる メインスレッド(「ボス」)、長い操作は存在しないはずです。これは、新しいリクエストの生成である可能性があるため、別のスレッドにシフトする必要があります。その結果、リクエスト自体の送信は次のようになります。



 private class StressClientHandler extends SimpleChannelUpstreamHandler { .... @Override public void channelConnected(ChannelHandlerContext ctx, final ChannelStateEvent e) throws Exception { ... requestExecutor.execute(new Runnable() { @Override public void run() { e.getChannel().write(requestSource.next()); } }); .... } }
      
      





エラー処理


次は、リクエストを送信する現在の頻度が高すぎる場合に新しい接続を作成するためのエラー処理です。 そして、これは最も重要な部分です。むしろ、プラットフォームに依存しないことは困難です。 この状況では、異なるオペレーティングシステムの動作が異なります。 たとえば、linuxはBindExceptionをスローし、windowsはConnectExceptionをスローし、MacOS Xはこれらのいずれか、または一般的にInternalError(Too many open files)をスローします。 T.O. m軸では、ストレスは最も予測不能に動作します。



この点で、接続中のエラー処理に加えて、ハンドラーでこれを行う必要があります(同時に統計のエラー数をカウントします):

 private class StressClientHandler extends SimpleChannelUpstreamHandler { .... @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { e.getChannel().close(); Throwable exc = e.getCause(); ... if (exc instanceof BindException) { be.incrementAndGet(); processLimitErrors(); } else if (exc instanceof ConnectException) { ce.incrementAndGet(); processLimitErrors(); } ... } .... }
      
      





サーバーの応答


最後に、サーバーからの応答をどう処理するかを決定する必要があります。 これはストレステストであり、帯域幅だけが重要であるため、統計情報を読み取るだけです。



 private class StressClientHandler extends SimpleChannelUpstreamHandler { @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { ... ChannelBuffer resp = (ChannelBuffer) e.getMessage(); received.incrementAndGet(); ... } }
      
      







HTTP応答のタイプ(4xx、2xx)の計算もあります。

全コード


ファイル、テンプレートエンジン、タイムアウトなどからのhttpテンプレートの読み取りなどの追加機能を備えたすべてのコード GitHub(究極のストレス)の完成したMavenプロジェクトの形式にあります。 完成した配布キット (jarファイル)をダウンロードできます。



結論




もちろん、開いている接続の制限にすべて反します。 たとえば、Linuxでは一部のOS設定(ulimitなど)を増やしながら、ローカルマシンでは最新のハードウェアで約30K rpsを達成できました。 理論的には、接続とネットワークの制限に加えて、これ以上の制限はありませんが、実際にはオーバーヘッドjvmが感じられ、実際のrpsは指定されたものより20〜30%少なくなります。



All Articles