JMeterを使ってRedisの性能テストをする方法(JMeter TCP SamplerからCRLFで終わるリクエストを送る
Redisが使えるか(うちのシステム的にって意味で)検証するため、JMeter使って性能を検証しましょうってことで環境構築しました。
まぁ、仕事でやってることなので、検証結果とかは公開できませんが、
とりあえず、JMeter TCP Samplerを使って、Redisと通信するための方法を自分の備忘録も兼ねて残しておきます。
JMeterを使ってRedisと通信するには、JMeter TCP Smplerを使います。
JMeter 2.7 r1342401には標準でTCP Samplerが付属しているので、
Apache JMeter - Downloads落としてきて、適当なところに展開するだけで使えます。
このTCP Sampler日本語での解説サイトが少ないので、ちょっとだけ細かく記述します。
スレッドグループやコントローラー配下に、追加→サンプラー→TCPサンプラーを選択して追加します。
すると、名前以外の欄が空の状態のTCPサンプラーの画面が表示されます。
TCPClient classnameには、org.apache.jmeter.protocol.tcp.sampler.AbstractTCPClientを実装したクラスを指定します。
JMeter 2.7 r1342401には標準でorg.apache.jmeter.protocol.tcp.sampler.TCPClientImpl、
org.apache.jmeter.protocol.tcp.sampler.BinaryTCPClientImplなどが付属しており、TCPClient classnameに何も入力しないと、jmeter.propertiesのtcp.handlerで指定されているクラスが使われるようです。
(コメントアウトされているが、TCPClientImplが使われるようです)
他の欄は。。。まぁ、そのままの内容なので説明は割愛します。
Redisの場合、標準設定ではポート6379を使うので、ポート番号には「6379」を入力します。
今回は解説ということもあって、Windows版のRedisをDownloads · dmajkic/redis · GitHubから落として来て使いますので、サーバ名またはIPには、「localhost」を指定しています。
先ずはTCPClientImplを使ってみます。
Redisのプロトコルはプロトコル仕様 — redis 2.0.3 documentationにあるように、UTF-8のテキストをコマンドとして送受信しますので、
送信するテキストには「PING」コマンドを送信するように「PING<改行>」を入力します。
これでRedisサーバーを起動すればテスト実施準備が終わります。
テストを実行してみると分るのですが、このままだとテストが終わりません。(タイムアウトを指定している場合はReadExceptionでタイムアウトします)
ログを見てみると、デフォルトでキャラクターセットは「Windows-31j」となっています。
が、コマンド自体はASCII文字だけなので、SETコマンドやLPUSHコマンドのキー名やデータ部分に非ASCII文字を送らない限りは問題はないでしょうから、
とりあえずこのままでいきます。
そもそも、PuttyからRaw接続で繋いでmonitorコマンドを入力して監視しても、Redis側には何も送られていないように見えます。
設定ファイルを弄っても変わらないので、Redisサーバーを終了して、Scalaで試しにポート6379に送られてくる内容を標準出力にダンプしてみました。
DummyServer.scala
/** * */ package tv.dyndns.poad import java.nio.channels.ServerSocketChannel import java.net.InetSocketAddress import scala.actors.Actor import java.nio.channels.SocketChannel import java.nio.ByteBuffer import java.nio.charset.Charset /** * @author Ken * */ class DummyServer(host : String, port : Int) extends Actor { private val channel = ServerSocketChannel.open(); def act : Unit = { try { channel.socket().bind(new InetSocketAddress(host, port)) println("start server " + host + ":" + port) while (true) { new Acceptor(channel.accept()).start() } } finally { channel.close() } } def close() : Unit = { if (channel.isOpen()) { channel.close() } } } class Acceptor(channel : SocketChannel) extends Actor { def act : Unit = { val buf = ByteBuffer.allocate(4096 * 2) loop { if (channel.read(buf) < 0) { exit } buf.flip() val decoded = Charset.forName("UTF-8").decode(buf).toString() println(decoded) decoded.getBytes().foreach { e => print("0x%02X ".format(e)) } println() buf.clear() buf.put("+OK".getBytes()) channel.close() exit } } }
DummyServerMain.scala
package tv.dyndns.poad import scala.io.Source object DummyServerMain extends App { val server = new DummyServer("localhost", 6379) try { server.start() while(true) { val line = readLine() if (line == "exit") { exit } } } finally { server.close() } }
出力結果はこんな感じ。
start server localhost:6379
PING
0x50 0x49 0x4E 0x47 0x0A
JMeterが送っている内容を見てみると、改行文字として<LF>しか送ってません。
Redisは>CRLF<なので、正しいコマンドとして認識しておらず応答してくれなかったようです。
そこで、BinaryTCPClientImplを使ってデータを送ってみます。
BinaryTCPClientImplでは、送信データにはHEX文字列を入力する必要があるそうです。
これでテストを流してみると、Redis側はちゃんと応答するようになります。
ですが、テストが通りません。応答は返ってきているのですが、タイムアウトとなります。
原因は、JMeter側での応答データ終了の検出設定にあります。
jmeter.propertiesにtcp.BinaryTCPClient.eomByte=10を追記します。
※ データ終了を表す値(1byte)を10進数表記で記述します。
すると、見事にテストが通りました。
まとめ
- TCPClientImplは終了文字としてLFしか送らない
- バイナリーデータを送るにはBinaryTCPClientImplを使う
- BinaryTCPClientで送るデータはHEX文字列を設定する
- BinaryTCPClientの応答データの終了検出はjmeter.propertiesにtcp.BinaryTCPClient.eomByteとして10進数表記で応答データの最終1byteの値を設定する