The following sections show examples of implementing your test in both Scala and Java. See Code Annotations for the Examples for information about either example.
To implement a load test in Scala, you must extend the Iago server's RecordProcessor
class to specify how to map transactions into the requests that the Iago server delivers to your service. The following example shows a RecordProcessor
subclass that implements a load test on a HTTP service:
package com.twitter.example
import com.twitter.finagle.http.Response
import com.twitter.iago.processor.RecordProcessor // 1
import com.twitter.iago.server.{ParrotService, ParrotRequest} // 2
import com.twitter.iago.util.Uri
import com.twitter.logging.Logger
class WebLoadTest(service: ParrotService[ParrotRequest, Response]) extends RecordProcessor {
val log = Logger(getClass)
override def processLine(line: String) { // 5
val request = new ParrotRequest( // 4
uri = Uri(line, Nil)
)
service(request) onSuccess { response => // 6
response.statusCode match { // 8
case x if 200 until 300 contains x => log.info("%s OK".format(response.statusCode))
case _ => log.error("%s: %s".format(response.statusCode, response.status.reason))
}
} onFailure { thrown =>
log.error(thrown, "%s: %s".format("URL", thrown.getMessage))
}
}
}
Then you must extend the Iago's FinagleParrotConfig
class and override recordProcessor
with your RecordProcessor
implementation:
package com.twitter.example
import com.twitter.iago.util.{RequestDistribution, SlowStartPoissonProcess}
import com.twitter.iago.processor.RecordProcessor
import com.twitter.iago.{FinagleParrotConfig, ParrotServerFlags}
import com.twitter.util.TimeConversions._
class WebLoadTestConfig(config: ParrotServerFlags) extends FinagleParrotConfig(config) {
override val recordProcessor: RecordProcessor = new WebLoadTest(service)
override def distribution(rate: Int): RequestDistribution = new SlowStartPoissonProcess(rate, 1.minute) // 9
}
To implement a Thrift load test in Scala, you must extend the Iago server's ThriftRecordProcessor
class to specify how to map transactions into the requests that the Iago server delivers to your service. The following example shows a ThriftRecordProcessor
subclass that implements a load test on an EchoService
Thrift service:
package com.twitter.example
import com.twitter.finagle.RichClientParam
import com.twitter.finagle.stats.DefaultStatsReceiver
import com.twitter.iago.processor.ThriftRecordProcessor // 1
import com.twitter.iago.server.{ParrotRequest, ParrotService} // 2
import com.twitter.logging.Logger
import thrift.EchoService
class EchoLoadTest(parrotService: ParrotService[ParrotRequest, Array[Byte]]) extends ThriftRecordProcessor(parrotService) {
private val statsReceiver = DefaultStatsReceiver.scope("echoservice_loadtest")
val client = new EchoService.FinagledClient( // 3
service,
RichClientParam(clientStats = statsReceiver)
)
val log = Logger.get(getClass)
override def processLine(line: String) { // 5
client.echo(line) respond { rep => // 6
if (rep == "hello") {
client.echo("IT'S TALKING TO US") // 7
}
log.info("response: " + rep) // 8
}
}
}
Then you must extend the Iago's ThriftParrotConfig
class and override recordProcessor
with your ThriftRecordProcessor
implementation:
import com.twitter.iago.{ThriftParrotConfig, ParrotServerFlags}
class EchoLoadTestConfig(config: ParrotServerFlags) extends ThriftParrotConfig(config) {
override val recordProcessor = new EchoLoadTest(service)
}
To implement a load test in Java, you must extend the Iago server's LoadTest
class to specify how to map transactions into the requests that the Iago server delivers to your service. The LoadTest
class provides Java-friendly type mappings for the underlying Scala internals. The following example shows a LoadTest
subclass that implements a load test on a HTTP service:
package com.twitter.jexample;
import java.util.List;
import scala.Tuple2;
import scala.collection.mutable.ArrayBuffer;
import com.twitter.finagle.http.FormElement;
import com.twitter.finagle.http.Response;
import com.twitter.finagle.thrift.ThriftClientRequest;
import com.twitter.iago.processor.LoadTest; // 1
import com.twitter.iago.server.ParrotRequest; // 2
import com.twitter.iago.server.ParrotService; // 2
import com.twitter.iago.util.Uri;
import com.twitter.util.Future;
import com.twitter.util.FutureEventListener;
import com.twitter.util.Promise;
public class WebLoadTest extends LoadTest {
private ParrotService<ParrotRequest, Response> service = null;
public WebLoadTest(ParrotService<ParrotRequest, Response> parrotService) {
this.service = parrotService;
}
public void processLine(String line) { // 5
ParrotRequest request = new ParrotRequest( // 4
scala.Some.apply(new Tuple2<String, Object>("www.google.com", 80)),
new ArrayBuffer<Tuple2<String, String>>(),
new Uri(line, new ArrayBuffer<Tuple2<String, String>>()),
"",
scala.Option.apply(null),
new ThriftClientRequest(new byte[1], false),
new Promise(),
new ArrayBuffer<Tuple2<String, String>>(),
"GET",
"",
false,
new ArrayBuffer<FormElement>(),
1
);
Future<Response> future = service.apply(request); // 6
future.addEventListener(new FutureEventListener<Response>() { // 8
public void onSuccess(Response resp) {
if(resp.statusCode() >= 200 && resp.statusCode() < 300) {
System.out.println(String.valueOf(resp.statusCode()) + " OK");
} else {
System.out.println("Error: " + String.valueOf(resp.statusCode()) + " " + resp.status().reason());
}
}
public void onFailure(Throwable cause) {
System.out.println("Error: " + cause.toString());
}
});
}
}
Then you must extend the Iago's FinagleParrotConfig
class and override recordProcessor
with your LoadTest
implementation:
package com.twitter.jexample;
import com.twitter.iago.processor.RecordProcessor;
import com.twitter.iago.FinagleParrotConfig;
import com.twitter.iago.ParrotServerFlags;
import com.twitter.iago.server.FinagleTransport;
public class WebLoadTestConfig extends FinagleParrotConfig {
public WebLoadTestConfig(ParrotServerFlags config) {
super(config);
}
@Override
public FinagleTransport transport() {
return super.transport();
}
@Override
public RecordProcessor recordProcessor() {
return new WebLoadTest(service());
}
}
To implement a Thrift load test in Java, you must extend the Iago server's ThriftLoadTest
class to specify how to map transactions into the requests that the Iago server delivers to your service. The ThriftLoadTest
class provides Java-friendly type mappings for the underlying Scala internals. The following example shows a ThriftLoadTest
subclass that implements a load test on an EchoService
Thrift service:
package com.twitter.jexample;
import com.twitter.example.thrift.EchoService;
import com.twitter.finagle.stats.NullStatsReceiver;
import com.twitter.iago.processor.ThriftLoadTest; // 1
import com.twitter.iago.server.ParrotRequest; // 2
import com.twitter.iago.server.ParrotService; // 2
import com.twitter.util.Future;
import com.twitter.util.FutureEventListener;
import org.apache.thrift.protocol.TBinaryProtocol;
import java.util.List;
public class EchoLoadTest extends ThriftLoadTest {
EchoService.FinagledClient client = null;
public EchoLoadTest(ParrotService<ParrotRequest, byte[]> parrotService) {
super(parrotService);
client = new EchoService.FinagledClient( // 3
service(),
new TBinaryProtocol.Factory(),
"EchoService",
new NullStatsReceiver());
}
public void processLine(String line) { // 5
Future<String> future = client.echo(line); // 6
future.addEventListener(new FutureEventListener<String>() { // 8
public void onSuccess(String msg) {
System.out.println("response: " + msg);
}
public void onFailure(Throwable cause) {
System.out.println("Error: " + cause);
}
});
}
}
Then you must extend the Iago's ThriftParrotConfig
class and override recordProcessor
with your ThriftLoadTest
implementation:
package com.twitter.jexample;
import com.twitter.iago.ParrotServerFlags;
import com.twitter.iago.ThriftParrotConfig;
import com.twitter.iago.processor.RecordProcessor;
public class EchoLoadTestConfig extends ThriftParrotConfig {
public EchoLoadTestConfig(ParrotServerFlags config) {
super(config, true);
}
@Override
public RecordProcessor recordProcessor() {
return new EchoLoadTest(service());
}
}
You define your Iago subclass to execute your service and map transactions to requests for your service:
- Import
com.twitter.iago.processor.RecordProcessor
(Scala) orLoadTest
(Java), whose instance will be executed by a Iago server. - Import
com.twitter.iago.server.ParrotService
andcom.twitter.iago.server.ParrotRequest
- For Thrift service, create an instance of your service to be placed under test.
- For Http service, create an instance of ParrotRequest to contain your http request.
- Override
processLine
method to format the request and execute your service. - Iago adds the request to its request queue.
- Optionally, you can initiate a new request based on the response to a previous one.
- Optionally, do something with the response. In this example, the response is logged.
- Optionally, overrides the
distribution
method to change the traffic pattern of the load test.