diff --git a/r2-int-test/build.gradle b/r2-int-test/build.gradle index 5da8db8c55..21719a4db7 100644 --- a/r2-int-test/build.gradle +++ b/r2-int-test/build.gradle @@ -5,41 +5,3 @@ dependencies { testCompile project(path: ':r2', configuration: 'testArtifacts') testCompile externalDependency.testng } - -// Build tasks for running PRPC and HTTP servers and perf tests -['Http'].each { proto -> - def props = System.properties.findAll { k,_ -> k.startsWith('perf.') } - - // Define server tasks - task("run${proto}Server", dependsOn: 'testClasses', type: JavaExec) { - def gclogdir = rootDir.toString() + '/build/r2-int-test/logs/gc' - mkdir (gclogdir) - jvmArgs '-verbose:gc', '-Xloggc:'+gclogdir+'/gc.log', '-XX:+PrintGCDetails', '-XX:+PrintGCDateStamps' - main = "test.r2.perf.driver.Run${proto}Server" - description = "Runs the ${proto} server" - classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath - systemProperties += props - }.doFirst { println "\n=== Starting ${proto} server ===\n" } - - // Define client tasks - ['Rpc', 'Rest'].each { style -> - task("run${proto}${style}Client", dependsOn: 'testClasses', type: JavaExec) { - main = "test.r2.perf.driver.Run${proto}${style}Client" - description = "Runs ${proto} client for ${style.toUpperCase()}" - classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath - systemProperties += props - }.doFirst { println "\n=== Starting ${proto} ${style.toUpperCase()} client ===\n" } - } - - // Define perf test tasks - ['Rpc', 'Rest'].each { style -> - task("perf${proto}${style}", dependsOn: 'testClasses', type: JavaExec) { - main = "test.r2.perf.driver.GradlePerfTestDriver" - args = ["test.r2.perf.driver.Run${proto}Server", "test.r2.perf.driver.Run${proto}${style}Client"] - description = "Runs ${style.toUpperCase()} perf tests for ${proto}" - classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath - systemProperties += props - }.doFirst { println "\n=== Starting perf test for ${proto} ${style.toUpperCase()} ===\n" } - } - -} diff --git a/r2-int-test/src/test/java/test/r2/perf/driver/GradlePerfTestDriver.java b/r2-int-test/src/test/java/test/r2/perf/driver/GradlePerfTestDriver.java deleted file mode 100644 index f4c52edca6..0000000000 --- a/r2-int-test/src/test/java/test/r2/perf/driver/GradlePerfTestDriver.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright (c) 2012 LinkedIn Corp. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/* $Id$ */ -package test.r2.perf.driver; - -import java.lang.reflect.Method; - -/** - * Very, very hacky way to run the client and the server - this is a stop gap until Gradle supports - * backgrounding java processes. - * - * @author Chris Pettitt - * @version $Revision$ - */ -public class GradlePerfTestDriver -{ - public static void main(String[] args) throws Exception - { - if (args.length != 2) - { - System.err.println("Usage: GradlePerfTestDriver "); - System.exit(1); - } - - final Class serverClass = Class.forName(args[0]); - final Class clientClass = Class.forName(args[1]); - - final Runnable serverThread = createRunnable(serverClass); - final Runnable clientThread = createRunnable(clientClass); - - serverThread.run(); - - clientThread.run(); - - System.exit(0); - } - - private static Runnable createRunnable(Class clazz) throws NoSuchMethodException - { - final Method method = clazz.getMethod("main", String[].class); - return new Runnable() { - @Override - public void run() - { - try - { - method.invoke(null, new Object[] {new String[0]}); - } - catch (Exception e) - { - e.printStackTrace(); - } - } - }; - } -} diff --git a/r2-perf-test/build.gradle b/r2-perf-test/build.gradle index 8725f6c51b..215ca3e0f5 100644 --- a/r2-perf-test/build.gradle +++ b/r2-perf-test/build.gradle @@ -5,31 +5,60 @@ dependencies { compile project (':r2-sample') compile project (':pegasus-common') compile project (':test-util') + testRuntime externalDependency.disruptor } -task runR2PerfTest (type: JavaExec) { - // Command line example: - /* gradle runPerfTest -Dperf.host=localhost -Dperf.port=8083 -Dperf.uri=/echo -Dperf.datafile="/home/usr/r2-perf-test/src/main/resources/data/group.json" -Dperf.reportfile="R2PerfTest.log" -Dperf.threads=10 -Dperf.runs=10 -Dperf.rampup=10 -S - */ - // This task is used for debugging JMeter JavaSample classes - def logdir = rootDir.toString() + '/build/r2-perf-test/logs' - mkdir (logdir) - def properties = System.properties.findAll { k,_ -> k.startsWith('perf.') } - main = 'test.r2.perf.R2Perf' - description = "Runs simple rest client performance tests" - classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath - systemProperties += properties +// Build tasks for running PRPC and HTTP servers and perf tests +['Http'].each { proto -> + def props = System.properties.findAll { k,_ -> k.startsWith('perf.') } + + // Define server tasks + task("run${proto}Server", dependsOn: 'testClasses', type: JavaExec) { + def gclogdir = rootDir.toString() + '/build/r2-perf-test/logs/gc' + mkdir (gclogdir) + jvmArgs '-verbose:gc', '-Xloggc:'+gclogdir+'/gc.log', '-XX:+PrintGCDetails', '-XX:+PrintGCDateStamps' + main = "test.r2.perf.driver.Run${proto}Server" + description = "Runs the ${proto} server" + classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath + systemProperties += props + maxHeapSize = "512m" + minHeapSize = "512m" + }.doFirst { println "\n=== Starting ${proto} server ===\n" } + + // Define client tasks + ['Rpc', 'Rest'].each { style -> + task("run${proto}${style}Client", dependsOn: 'testClasses', type: JavaExec) { + def gclogdir = rootDir.toString() + '/build/r2-perf-test/logs/gc' + jvmArgs '-verbose:gc', '-Xloggc:'+gclogdir+'/client-gc.log', '-XX:+PrintGCDetails', '-XX:+PrintGCDateStamps' + main = "test.r2.perf.driver.Run${proto}${style}Client" + description = "Runs ${proto} client for ${style.toUpperCase()}" + classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath + systemProperties += props + maxHeapSize = "512m" + minHeapSize = "512m" + }.doFirst { println "\n=== Starting ${proto} ${style.toUpperCase()} client ===\n" } + } } -task runHttpPerfServer (type: JavaExec) { - def props = System.properties.findAll { k,_ -> k.startsWith('perf.') } - classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath - - def gclogdir = rootDir.toString() + '/build/r2-perf-test/logs/gc' - mkdir (gclogdir) - jvmArgs '-verbose:gc', '-Xloggc:'+gclogdir+'/gc.log', '-XX:+PrintGCDetails', '-XX:+PrintGCDateStamps' - main = 'test.r2.perf.RunHttpServer' - description = "Runs the http server" - classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath - systemProperties += props +task("perf", dependsOn: 'testClasses', type: Exec) { + workingDir rootDir.path + File.separator + 'r2-perf-test' + executable '../gradlew' + args 'runHttpRestClient' + // without this, if test fails, doLast will not execute, the server may keep running in daemon + ignoreExitValue = true + + doFirst { + classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath + final URL[] classpathUrls = classpath.collect { it.toURI().toURL() } as URL[] + it.ext.contextClassLoader = Thread.currentThread().contextClassLoader + Thread.currentThread().contextClassLoader = new URLClassLoader(classpathUrls) + it.ext.serverClass = Thread.currentThread().contextClassLoader.loadClass('test.r2.perf.driver.RunHttpServer') + + it.ext.serverClass.main(new String[0]) + } + doLast { + it.ext.serverClass.stop() + it.execResult.assertNormalExitValue() + Thread.currentThread().contextClassLoader = it.ext.contextClassLoader + } } \ No newline at end of file diff --git a/r2-perf-test/src/R_scripts/R2_summary_and_density_plot.R b/r2-perf-test/src/R_scripts/R2_summary_and_density_plot.R index f067040486..813507d62d 100644 --- a/r2-perf-test/src/R_scripts/R2_summary_and_density_plot.R +++ b/r2-perf-test/src/R_scripts/R2_summary_and_density_plot.R @@ -7,86 +7,47 @@ graphics.off() # Close graphics windows args <- commandArgs(TRUE) # retrieve args -print (args) - dataFileOrDir <- args[1] -output_graph_file <- args[2] -outputSummaryHtmlFile <- args[3] -outputSummaryJsonFile <- args[4] -rampupTime <- args[5] # in seconds +output_graph_file <- "response_time" +outputSummaryHtmlFile <- "summary.html" +outputSummaryJsonFile <- "summary.json" ttlresponses <- 0 successcount <- 0 failedcount <- 0 -# Get list of log files -datafiles <- list.files(dataFileOrDir, pattern="*.log") - -regexp <- "^(\\d{4}\\/\\d{2}\\/\\d{2} \\d{1,2}:\\d{1,2}:\\d{1,2}.\\d{3}) \\[\\(\\w+[^,]*,([^,]*),[^\\]]*\\](?:\\[[^\\]]*\\])*\\] \\[([^\\]]*)\\] \\[([^\\]]*)\\] ([OKFAILED].*)\\(threadid:(\\d+),.*\\) in (\\d+).?(\\d+)?ms\\s{0,1}$" # --- Functions getAxisTimeFrequency <- function(ttlexecutiontime) { - print(ttlexecutiontime) atfrequency <- 1; if (ttlexecutiontime/60 > 1 ) { atfrequency <- ttlexecutiontime/60; } - cat("\natfrequency:",atfrequency) atfrequency } -readDataFiles <- function(dataFileDir, datafileslist) -{ - cat ("\n\ndataFileDir:",dataFileDir) - cat ("\n\ndatafileslist:",datafileslist) - extracteddata <- NULL - ttlresp <- 0 - for (file in c(datafiles)){ - datafile <- paste(dataFileDir, "/", file, sep = "") - - dfile <- file(datafile) - filedata <- readDataFile (datafile) - extracteddata <- rbind(extracteddata, filedata) - rm(filedata) - close(dfile) - } - extracteddata -} - -readDataFile <- function(datafile) +readCsvFile <- function(datafile) { - cat ("\n Parsing data from ",datafile," file") - extracteddata <- NULL - dfile <- file(datafile) - data <- readLines(dfile) + cat ("\nReading data from ",datafile, "\n\n") + data <- read.csv(datafile) - # Get response times - resp_times <- as.double(c(gsub(pattern = regexp, replacement = "\\7.\\8", x = data, perl = TRUE))) # Get Time - dates <- strptime(gsub(regexp, replacement = "\\1", data, perl = TRUE),"%Y/%m/%d %H:%M:%OS", tz="") - # Get threads - fthreads <- gsub(pattern = regexp, replacement = "\\6", x = data, perl = TRUE) - # Get status - status <- gsub(pattern = regexp, replacement = "\\5", x = data, perl = TRUE) + dates <- strptime(data$date, "%Y/%m/%d %H:%M:%OS", tz="") + # Get response times + resptimes <- data$latency_nano / 10E6 # Create data frame with all responses (including failed) - alldata <- data.frame(date=dates, resptime=resp_times, threads=fthreads) - - close(dfile) + alldata <- data.frame(date=dates, resptime=resptimes) alldata } plotResponseTimeVsTime <- function (data, starttime, endtime, axistimefrequency,gtitle) { x <- strptime(data$date,"%Y-%m-%d %H:%M:%OS", tz="") - y <- data$resptime - - # starttime and endtime should be passed as as.POSIXct(starttime), as.POSIXct(endtime) - cat("\n axistimefrequency:", axistimefrequency,"\n") - cat("\n startime:",starttime," endtime:",endtime,"\n") + y <- data$resptime xpos <- plot( x, # Timestamp @@ -179,28 +140,11 @@ op <- options(digits.secs=4) r2stats <- NULL -if ( length(datafiles)[1] == 0 ) -{ - r2stats <- readDataFile(dataFileOrDir) -} else -{ - r2stats <- readDataFiles(dataFileOrDir, datafiles) -} - -#attach(r2stats) +r2stats <- readCsvFile(dataFileOrDir) dates <- strptime(r2stats$date,"%Y-%m-%d %H:%M:%OS", tz="") starttime <- min(as.POSIXct(dates)) -if (is.null(rampupTime) == FALSE && rampupTime > 0) -{ - newstarttime <- as.numeric(starttime) + as.numeric(rampupTime) - r2stats <- r2stats[as.POSIXct(r2stats$date) > newstarttime,] - r2stats - dates <- strptime(r2stats$date,"%Y-%m-%d %H:%M:%OS", tz="") - starttime <- min(as.POSIXct(dates)) -} - attach(r2stats) endtime <- max(dates) @@ -212,20 +156,9 @@ ttlresponses <- length(r2stats$resptime) respcount <- length(r2stats$resptime) # Get test details for summary report in graph legend -threadfactor <- factor(r2stats$threads) -totalthreads <- length(levels(threadfactor)) successcount <- length(successData$resptime) # only successful responses failedcount <- ttlresponses - successcount #length(r2stats[is.na(r2stats$resptime),]) # ttlresponses - successcount -print(r2stats[ is.na(r2stats$resptime),]) - -cat("\nTOTAL RESPONSES:",ttlresponses) -cat("\nTOTAL SUCCESSFUL RESPONSES:",successcount) -cat("\nTOTAL FAILED RESPONSES:",failedcount) -cat("\nTOTAL THREADS:",totalthreads) -cat("\nStats:") -summary(r2stats) - ### Calculate mean and median response time values for all responses sums <- c(summary(successData$resptime)) @@ -235,12 +168,12 @@ minresptime <- sums[1] maxresptime <- sums[6] frsq <- sums[2] # 1st. Qu trdq <- sums[5] # 3rd Qu. +percentile50 <- quantile(successData$resptime, 0.5) +percentile90 <- quantile(successData$resptime, 0.9) percentile95 <- quantile(successData$resptime, 0.95) -percentile98 <- quantile(successData$resptime, 0.98) +percentile99 <- quantile(successData$resptime, 0.99) standard_deviation <- sapply(successData,sd) -print(standard_deviation) - #Calculate throughput howLongRunning <- difftime(as.POSIXct(endtime), as.POSIXct(starttime), tz="", units="secs") axistimefrequency <- getAxisTimeFrequency (as.numeric(howLongRunning, units="secs")) @@ -251,6 +184,19 @@ successData <- NULL graphHeight=1100 ttlPlots=2 +cat("\nTotal Responses:",ttlresponses) +cat("\nTotal Successful Responses:",successcount) +cat("\nTotal Failed Responses:",failedcount) +cat("\nStats:") +cat("\n\tMean latency (in millis): ", mean(r2stats$resptime)) +cat("\n\tReqs / Sec: ", nrow(r2stats) / as.numeric(howLongRunning)) +cat("\n\tMin Latency: ", min(r2stats$resptime)) +cat("\n\t50% Latency: ", percentile50) +cat("\n\t90% Latency: ", percentile90) +cat("\n\t95% Latency: ", percentile95) +cat("\n\t99% Latency: ", percentile99) +cat("\n\tMax Latency: ", max(r2stats$resptime)) + # If test run is 4+ minutes long, plot 1 minute graph in addition to main and density graphs mid <- round(min(length(dates)/2)) if ((endtime - (dates[mid]+60)) > 0) @@ -268,7 +214,7 @@ plot.new() # start a new plot par(mar=c(24,5,5,5)+0.1) # set the size of the outer margins par(mfrow=c(ttlPlots,1)) # plot three graphs in one page -cat("\nPlotting first graph: Response Time vs Execution Time\n") +cat("\n\nPlotting first graph: Response Time vs Execution Time\n") # First plot: response time vs time # @@ -282,19 +228,6 @@ cat("\nPlotting second graph: Density\n") # plotDensityGraph(r2stats$resptime,resptimedensity) -cat("\nPlotting third graph: One minute graph\n") - -# Third plot - one minute graph -# - -if ((endtime - (dates[mid]+60)) > 0) -{ - print(as.POSIXlt(dates[mid])) - print(as.POSIXlt(dates[mid]+60)) - #plotResponseTimeVsTime(dates, r2stats$resptime, as.POSIXct(as.POSIXlt(dates[mid])), as.POSIXct(as.POSIXlt(dates[mid]+60)), 1, paste("One Minute Snapshot (",dates[mid]," ",dates[mid]+60,")")) - plotResponseTimeVsTime(r2stats, as.POSIXct(as.POSIXlt(dates[mid])), as.POSIXct(as.POSIXlt(dates[mid]+60)), 1, paste("One Minute Snapshot (",dates[mid]," ",dates[mid]+60,")")) -} - options(op) dev.off() @@ -304,12 +237,12 @@ out <- file(outputSummaryHtmlFile,"w") # open an output txt file htmldata <- c("") # Summary html table -summaryHeader <- c("Test Start Time","Test Stop Time","Total Execution Time(sec)","Total Responses","Successful Responses","Failed Responses","% Failed Responses","Total Threads","Throughput (r/sec") -summaryData <- c(format(starttime,"%Y/%m/%d %H:%M:%S", tz=""),format(endtime,"%Y/%m/%d %H:%M:%S", tz=""), howLongRunning, ttlresponses, successcount, failedcount, paste((failedcount*100)/ttlresponses," %"),totalthreads,throughput) +summaryHeader <- c("Test Start Time","Test Stop Time","Total Execution Time(sec)","Total Responses","Successful Responses","Failed Responses","% Failed Responses","Throughput (r/sec") +summaryData <- c(format(starttime,"%Y/%m/%d %H:%M:%S", tz=""),format(endtime,"%Y/%m/%d %H:%M:%S", tz=""), howLongRunning, ttlresponses, successcount, failedcount, paste((failedcount*100)/ttlresponses," %"),throughput) # Response Stats html table -responsesStatsHeader <- c("Min Response Time(millisec)","1st. Qu(millisec)","Median(millisec)","Mean(millisec)","3rd Qu.(millisec)","95%(millisec)","98%(millisec)","Max Response(millisec)","Standard Deviation") -responsesStatsData <- c(minresptime, frsq, resp_median, resp_mean, trdq, percentile95, percentile98, maxresptime, toString(standard_deviation)) +responsesStatsHeader <- c("Min Response Time(millisec)","1st. Qu(millisec)","Median(millisec)","Mean(millisec)","3rd Qu.(millisec)","90%(millisec)","95%(millisec)","99%(millisec)","Max Response(millisec)","Standard Deviation") +responsesStatsData <- c(minresptime, frsq, resp_median, resp_mean, trdq, percentile90, percentile95, percentile99, maxresptime, toString(standard_deviation)) cat(htmldata,"


SERVER REQUESTS


SUMMARY

",generateSummaryHtmlTable(summaryHeader,summaryData ),"


RESPONSE STATS

",generateSummaryHtmlTable(responsesStatsHeader,responsesStatsData ),c(" 0) -{ - newstarttime <- as.numeric(starttime) + as.numeric(rampupTime) - data <- data[as.POSIXct(data$Time) > newstarttime,] - data - times <- strptime(data$Time,"%Y-%m-%d %H:%M:%OS", tz="") - starttime <- min(as.POSIXct(times)) -} - endtime <- max(as.POSIXct(data$Time)) howLongRunning <- difftime(endtime,starttime , tz="", units="secs") cat("howlongRunning:",howLongRunning) diff --git a/r2-perf-test/src/main/java/test/r2/perf/MiscUtil.java b/r2-perf-test/src/main/java/test/r2/perf/MiscUtil.java deleted file mode 100644 index 0f243a0c7c..0000000000 --- a/r2-perf-test/src/main/java/test/r2/perf/MiscUtil.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - Copyright (c) 2012 LinkedIn Corp. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package test.r2.perf; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; - -public class MiscUtil implements TestConstants -{ - public static URI getUri(final String propName) - { - final String propVal = System.getProperty(propName); - return propVal != null ? URI.create(propVal) : URI.create(DEFAULT_RELATIVE_URI); - } - - /** - * If there is a system property with name {@code propName}, return it. Otherwise, return the - * default value. - * - * @param propName name of the property - * @param defaultValue - * @return value of the property - */ - public static String getString(final String propName, final String defaultValue) - { - final String propVal = System.getProperty(propName); - return propVal != null ? propVal : defaultValue; - } - - /** - * Completely read the given file and return the contents as a byte array. - * - * @param file the file to read from - * @return the content of the file - * @throws IOException - */ - public static byte[] getBytesFromFile(final File file) throws IOException - { - InputStream is = null; - try - { - is = new FileInputStream(file); - - final long length = file.length(); - - if (length > Integer.MAX_VALUE) - { - // File is too large - throw new IOException("File " + file + " is too large."); - } - - final byte[] bytes = new byte[(int) length]; - - int offset = 0; - int numRead = 0; - while (offset < bytes.length - && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) - { - offset += numRead; - } - - if (offset < bytes.length) - { - throw new IOException("Could not completely read file " + file.getName()); - } - return bytes; - } - finally - { - if (is != null) - is.close(); - } - } -} diff --git a/r2-perf-test/src/main/java/test/r2/perf/R2Perf.java b/r2-perf-test/src/main/java/test/r2/perf/R2Perf.java deleted file mode 100644 index cf71e09e17..0000000000 --- a/r2-perf-test/src/main/java/test/r2/perf/R2Perf.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - Copyright (c) 2012 LinkedIn Corp. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package test.r2.perf; - -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.util.Arrays; -import java.util.Collections; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.linkedin.common.callback.FutureCallback; -import com.linkedin.common.util.None; -import com.linkedin.r2.message.rest.RestRequest; -import com.linkedin.r2.message.rest.RestRequestBuilder; -import com.linkedin.r2.transport.common.Client; -import com.linkedin.r2.transport.common.bridge.client.TransportClientAdapter; -import com.linkedin.r2.transport.http.client.HttpClientFactory; - - -public class R2Perf implements TestConstants -{ - - private static final Logger _log = LoggerFactory.getLogger(R2Perf.class); - - private static final long serialVersionUID = 240L; - private static final HttpClientFactory FACTORY = new HttpClientFactory(); - public static final Client CLIENT = new TransportClientAdapter(FACTORY.getClient(Collections.emptyMap())); - - private String _host; - private int _port; - private String _relative_uri; - private String _datafile; - private String _header; - private double _delay; - private double _range; - private int _req_length; - private boolean _req_length_randomize; - private int _threads; - private int _runs; - private double _rampup; - - private byte[] _entity; - private URI _uri; - private RestRequest _request; - - public R2Perf() - { - setupValues(); - } - - private void setupValues() - { - _host = MiscUtil.getString(SERVER_HOST_PROP_NAME, DEFAULT_HOST); - _port = Integer.parseInt(System.getProperty(SERVER_PORT_PROP_NAME, DEFAULT_PORT)); - _relative_uri = MiscUtil.getString(SERVER_RELATIVE_URI_PROP_NAME, DEFAULT_RELATIVE_URI); - _datafile = MiscUtil.getString(DATA_FILE_PROP_NAME, DEFAULT_DATAFILE); - _header = DEFAULT_HEADER; - _delay = Double.parseDouble(System.getProperty(DELAY_PROP_NAME, DEFAULT_DELAY)); - _range = Double.parseDouble(System.getProperty(RANGE_PROP_NAME, DEFAULT_RANGE)); - _req_length = Integer.parseInt(System.getProperty(REQUEST_LENGTH_PROP_NAME, DEFAULT_REQUEST_LENGTH)); - _req_length_randomize = Boolean.parseBoolean(MiscUtil.getString(REQUEST_LENGTH_RANDOM_PROP_NAME, DEFAULT_REQUEST_LENGTH)); - _threads = Integer.parseInt(System.getProperty(THREADS_NUMBER_PROP_NAME, DEFAULT_THREADS)); - _runs = Integer.parseInt(System.getProperty(RUNS_NUMBER_PROP_NAME, DEFAULT_RUNS)); - _rampup = (Double.parseDouble(System.getProperty(RAMPUP_PROP_NAME, DEFAULT_RAMPUP))/_threads)*1000; - - System.out.println("perf.host:"+_host); - System.out.println("perf.port:"+_port); - System.out.println("perf.uri:"+_relative_uri); - System.out.println("perf.datafile:"+_datafile); - System.out.println("perf.delay:"+_delay); - System.out.println("perf.range:"+_range); - System.out.println("perf.threads:"+_threads); - System.out.println("perf.runs:"+_runs); - System.out.println("perf.rampup:"+_rampup); - } - - public void setupTest() - { - System.out.println("Executing setupTest... threadid:"+Thread.currentThread().getId()+",threadName:"+Thread.currentThread().getName()); - String method = "POST"; - - try - { - File file = new File(_datafile); - _entity = MiscUtil.getBytesFromFile(file); - if (_req_length != -1 && _req_length < _entity.length) - { - byte[] arr2 = Arrays.copyOf(_entity, _req_length); - _entity = Arrays.copyOf(arr2, _req_length); - } - } - catch (Exception e) - { - _log.error("Got exception in setupTest. Exception :"+e.getMessage()); - e.printStackTrace(); - } - - _uri = URI.create("http://" + _host + ":" + _port + _relative_uri); - try - { - _request = buildRequest(_uri, method, _entity) ; - } - catch (Exception e) - { - _log.info(",ERROR,,"+e.toString()); - } - } - - public void runTest() throws Exception - { - System.out.println( - " _datafile:" + _datafile + " _threads:" + _threads + " _runs:" + _runs + " _delay:" + _delay + " _range:" + _range); - R2PerfDriver.sendRequest(CLIENT, _uri, _request, _threads, _runs, _delay, - _range, _host, _relative_uri, _rampup); - } - - public void teardownTest() throws ExecutionException, TimeoutException, InterruptedException - { - shutdown(); - } - - private RestRequest buildRequest(URI uri, String method, byte[] entity) throws IOException - { - RestRequestBuilder requestBuilder = new RestRequestBuilder(uri).setMethod(method); - - requestBuilder.setHeader(DEFAULT_HEADER, "1"); - - if (entity != null) - { - requestBuilder.setEntity(entity).setHeader("Content-Type", "application/json"); - } - - return requestBuilder.build(); - } - - private void shutdown() throws ExecutionException, TimeoutException, InterruptedException - { - final FutureCallback callback = new FutureCallback(); - CLIENT.shutdown(callback); - - callback.get(30, TimeUnit.SECONDS); - _log.info("Client shutdown has been completed."); - - final FutureCallback factoryCallback = new FutureCallback(); - FACTORY.shutdown(factoryCallback, 30, TimeUnit.SECONDS); - factoryCallback.get(30, TimeUnit.SECONDS); - _log.info("Factory shutdown has been completed."); - } - - public static void main (String [] args) - { - R2Perf test = new R2Perf(); - test.setupTest(); - try - { - test.runTest(); - } - catch (Exception e) - { - e.printStackTrace(); - } - finally - { - try - { - test.teardownTest(); - } - catch (Exception e) - { - _log.error("Error tearing down test", e); - e.printStackTrace(); - // Even if some non-daemon threads fail to stop, System.exit() should terminate the VM - System.exit(-1); - } - } - } -} diff --git a/r2-perf-test/src/main/java/test/r2/perf/R2PerfDriver.java b/r2-perf-test/src/main/java/test/r2/perf/R2PerfDriver.java deleted file mode 100644 index fba0988c17..0000000000 --- a/r2-perf-test/src/main/java/test/r2/perf/R2PerfDriver.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - Copyright (c) 2012 LinkedIn Corp. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package test.r2.perf; - -import java.net.InetAddress; -import java.net.URI; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; - -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import java.lang.InterruptedException; - -import java.text.DecimalFormat; -import java.text.NumberFormat; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.linkedin.common.callback.FutureCallback; -import com.linkedin.r2.message.rest.RestRequest; -import com.linkedin.r2.message.rest.RestResponse; -import com.linkedin.r2.transport.common.Client; -import com.linkedin.test.util.GaussianRandom; - -public class R2PerfDriver -{ - private R2PerfDriver() - { - } - - private static final Logger _log = LoggerFactory.getLogger(R2PerfDriver.class); - - public static void sendRequest(Client client, URI uri, RestRequest request,final int numThreads, final int runs, final double delay,final double range, final String remote_host, final String relative_uri, final double rampup) - throws Exception - { - final BlockingQueue queue = new LinkedBlockingQueue(); - final ExecutorService service = - new ThreadPoolExecutor(numThreads, numThreads, 500, TimeUnit.MILLISECONDS, queue); - final String _current_host = InetAddress.getLocalHost().getHostName(); - - try - { - for (int i = 0; i < numThreads; i++) - { - service.submit(runnable(client, uri, request, runs/numThreads, delay, range, relative_uri, remote_host, _current_host, rampup)); - } - } - catch (Exception e) - { - e.printStackTrace(); - } - finally - { - service.shutdown(); - - if (!service.awaitTermination(600000000, TimeUnit.SECONDS)) - { - throw new Exception("Threadpool shutdown: Timed out waiting!"); - } - } - } - - private static Runnable runnable(final Client client, - final URI uri, - final RestRequest request, - final int runs, - final double delay, - final double range, - final String relative_uri, - final String remote_host, - final String current_host, - final double rampup) - throws InterruptedException - { - return new Runnable() - { - public void run() - { - double resptime = -1.0; - for (int i = 0; i < runs; i++) - { - if (i == 0 ) - { - // Rampup threads - try - { - long sleeptime = GaussianRandom.delay(rampup,rampup); - Thread.sleep(sleeptime); - } - catch (InterruptedException e) - { - } - System.out.println("Started "+" threadid:"+Thread.currentThread().getId()+",threadName:"+Thread.currentThread().getName()); - } - try - { - final FutureCallback callback = new FutureCallback(); - - try - { - if (delay > 0) - { - Thread.sleep(GaussianRandom.delay(delay,range)); - } - } catch (Exception e) - { - } - double start = System.nanoTime(); - sendMessage(client, request, callback); - try - { - RestResponse response = callback.get(); - resptime = (System.nanoTime() - start)/1000000; - int resplength = response.getEntity().length(); - if ( resplength > 0) - { - NumberFormat formatter = new DecimalFormat("####.##"); - _log.info(message("OK",String.valueOf(resplength)," in "+formatter.format(resptime)+"ms", relative_uri,remote_host, current_host)); - } - else - { - _log.info(message("FAILED",String.valueOf(resplength)," ,Failed to receive response."+errmsg(i), relative_uri,remote_host, current_host)); - } - } - catch (Exception e) - { - _log.info(message("FAILED","NA"," ,Failed to get RestResponse."+errmsg(i)+" Exception:"+e.getMessage(), relative_uri,remote_host, current_host)); - } - } - catch (Exception e) - { - _log.info(message("FAILED","NA"," ,Failed to get RestResponse."+errmsg(i)+" Exception:"+e.getMessage(), relative_uri,remote_host, current_host)); - } - } - } - }; - } - - private static void sendMessage(Client client, RestRequest request, FutureCallback callback) - { - client.restRequest(request, callback); - } - - private static String errmsg(int run) - { - return " run:"+String.valueOf(run)+" threadid:"+Thread.currentThread().getId()+",threadName:"+Thread.currentThread().getName(); - } - - private static String message(String status, String responselength, String resptimemsg, String relative_uri, String remote_host, String current_host) - { - try - { - return "[("+current_host+",SimpleRestClient,"+relative_uri+")["+remote_host+",r2d2,sendRequest]] [] [R2D2] "+status+"(threadid:"+Thread.currentThread().getId()+",threadName:"+Thread.currentThread().getName()+",responseLength:"+responselength+")"+resptimemsg; - } - catch (Exception e) - { - return null; - } - } -} - diff --git a/r2-perf-test/src/main/java/test/r2/perf/RunHttpServer.java b/r2-perf-test/src/main/java/test/r2/perf/RunHttpServer.java deleted file mode 100644 index 36f41706ba..0000000000 --- a/r2-perf-test/src/main/java/test/r2/perf/RunHttpServer.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - Copyright (c) 2012 LinkedIn Corp. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package test.r2.perf; - -import com.linkedin.r2.transport.common.Server; -import com.linkedin.r2.transport.common.bridge.server.TransportDispatcher; -import com.linkedin.r2.transport.common.bridge.server.TransportDispatcherBuilder; -import com.linkedin.r2.transport.http.server.HttpServerFactory; -import com.linkedin.r2.sample.echo.rest.RestEchoServer; -import com.linkedin.r2.sample.echo.EchoServiceImpl; - -import java.io.IOException; -import java.net.URI; - - -public class RunHttpServer implements TestConstants -{ - public static void main(String[] args) throws IOException - { - final int port = Integer.parseInt(System.getProperty(SERVER_PORT_PROP_NAME, DEFAULT_PORT)); - final URI relativeUri = MiscUtil.getUri(DEFAULT_RELATIVE_URI); - final TransportDispatcher dispatcher = new TransportDispatcherBuilder() - .addRestHandler(relativeUri, new RestEchoServer(new EchoServiceImpl())) - .build(); - - final Server server = new HttpServerFactory().createServer(port, dispatcher); - //final Server server = new HttpServerFactory().createServer(port, createDispatcher(relativeUri)); - server.start(); - } - - private static TransportDispatcher createDispatcher(URI uri) - { - return new TransportDispatcherBuilder().addRestHandler(uri, new RestEchoServer(new EchoServiceImpl())).build(); - } -} diff --git a/r2-perf-test/src/main/java/test/r2/perf/TestConstants.java b/r2-perf-test/src/main/java/test/r2/perf/TestConstants.java deleted file mode 100644 index fadec87921..0000000000 --- a/r2-perf-test/src/main/java/test/r2/perf/TestConstants.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - Copyright (c) 2012 LinkedIn Corp. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package test.r2.perf; - -import java.net.URI; - -public interface TestConstants -{ - // Properties names - static final String HEADER_PROP_NAME = "perf.header"; - static final String SERVER_HOST_PROP_NAME = "perf.host"; - static final String SERVER_PORT_PROP_NAME = "perf.port"; - static final String SERVER_RELATIVE_URI_PROP_NAME = "perf.uri"; - static final String DATA_FILE_PROP_NAME = "perf.datafile"; - static final String DELAY_PROP_NAME = "perf.delay"; - static final String RANGE_PROP_NAME = "perf.range"; - static final String REQUEST_LENGTH_PROP_NAME = "perf.request.length"; - static final String REQUEST_LENGTH_RANDOM_PROP_NAME = "perf.request.length.randomize"; - static final String THREADS_NUMBER_PROP_NAME = "perf.threads"; - static final String RUNS_NUMBER_PROP_NAME = "perf.runs"; - static final String RAMPUP_PROP_NAME = "perf.rampup"; - - // Default values - static final String DEFAULT_HOST = "localhost"; - static final String DEFAULT_PORT = "8083"; - static final String DEFAULT_RELATIVE_URI = "/echo"; - static final String DEFAULT_DATAFILE = ""; - static final String DEFAULT_LABEL = "SimpleRestClient"; - static final String DEFAULT_HEADER = "X-LinkedIn-Auth-Member"; - static final String DEFAULT_DELAY = "0.0"; - static final String DEFAULT_RANGE = "0.0"; - static final String DEFAULT_REQUEST_LENGTH = "-1"; - static final String DEFAULT_REQUEST_LENGTH_RANDOMIZE = "false"; - static final String DEFAULT_RESPONSE_CODE = ""; - static final String DEFAULT_RESPONSE_MESSAGE = ""; - static final String DEFAULT_THREADS = "1"; - static final String DEFAULT_RUNS = "1"; - static final String DEFAULT_RAMPUP = "0"; - -} diff --git a/r2-perf-test/src/main/resources/data/group.json b/r2-perf-test/src/main/resources/data/group.json deleted file mode 100644 index 6f92a26980..0000000000 --- a/r2-perf-test/src/main/resources/data/group.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "id":1, - "name":"Genentech Mad Scientists Club", - "vanityUrl":"genentech-mad-scientists-club", - "shortDescription":"Alfred Hitchcock is looking for 'psycho' mad scientists to join his group.", - "description":"Alfred Hitchcock is looking for 'psycho' mad scientists to join his group.", - "category":2, - "lastModifiedTimestamp":1300005945795, - "otherCategory":3, - "hideSubgroups":false, - "newsFormat":"RECENT", - "visibility":"PUBLIC", - "nonMemberPermissions":"COMMENT_AND_POST_WITH_MODERATION", - "location": { - "geoPlaceCodes":["1-2-3-4-5"], - "regionCode":37, - "postalCode":"94043", - "usesDaylightSavings":true, - "countryCode":"us", - "gmtOffset":-8.0, - "longitude":37.4, - "latitude":122.1, - "geoPostalCode":"94043" - }, - "homeSiteUrl":"http://www.example.com", - "locale":"en_US", - "state":"ACTIVE", - "directoryPresence":"PUBLIC", - "categoriesEnabled":"DISCUSSION", - "hasMemberInvites":false, - "smallLogoMediaUrl":"/0/0/1/skafhdsjahiuewh", - "maxSubgroups":20, - "hasEmailExport":true, - "maxMembers":2000, - "hasSettings":true, - "hasMemberRoster":true, - "hasNetworkUpdates":true, - "preModerateNewMembersPeriodInDays":3, - "preModerationCategories":"JOB", - "maxIdentityChanges":5, - "categoriesForModeratorsOnly":"DISCUSSION", - "approvalModes":1, - "sharingKey":"HJFD3JH98JKH3", - "preApprovedEmailDomains":["foo.com"], - "preModerateMembersWithLowConnections":true, - "createdTimestamp":1300005945794, - "contactEmail":"bob@example.com", - "contactability":"CONTACTABLE", - "numMemberFlagsToDelete":3, - "numIdentityChanges":5, - "preModeration":"COMMENTS", - "rules":"No spam, please", - "openedToNonMembersTimestamp":1300005945801, - "maxModerators":10, - "maxFeeds":100, - "badge":"FEATURED", - "isOpenToNonMembers":true, - "largeLogoMediaUrl":"/0/0/1/skafhdsjahiuewh", - "owner": { - "memberID": 1, - "contactEmail": "alfred@test.linkedin.com", - "firstName": "Alfred", - "lastName": "Hitchcock" - } -} diff --git a/r2-int-test/src/test/java/test/r2/perf/client/RequestGenerator.java b/r2-perf-test/src/test/java/test/r2/perf/Generator.java similarity index 91% rename from r2-int-test/src/test/java/test/r2/perf/client/RequestGenerator.java rename to r2-perf-test/src/test/java/test/r2/perf/Generator.java index 0422f6d6c8..3a51a1d3d2 100644 --- a/r2-int-test/src/test/java/test/r2/perf/client/RequestGenerator.java +++ b/r2-perf-test/src/test/java/test/r2/perf/Generator.java @@ -15,13 +15,13 @@ */ /* $Id$ */ -package test.r2.perf.client; +package test.r2.perf; /** * @author Chris Pettitt * @version $Revision$ */ -public interface RequestGenerator +public interface Generator { T nextMessage(); } diff --git a/r2-int-test/src/test/java/test/r2/perf/PerfConfig.java b/r2-perf-test/src/test/java/test/r2/perf/PerfConfig.java similarity index 93% rename from r2-int-test/src/test/java/test/r2/perf/PerfConfig.java rename to r2-perf-test/src/test/java/test/r2/perf/PerfConfig.java index d318aca2a4..d80194f120 100644 --- a/r2-int-test/src/test/java/test/r2/perf/PerfConfig.java +++ b/r2-perf-test/src/test/java/test/r2/perf/PerfConfig.java @@ -33,6 +33,7 @@ public class PerfConfig private static final String PERF_CLIENT_MSG_SIZE = "perf.client.msg_size"; private static final String PERF_RELATIVE_URI = "perf.relative_uri"; private static final String PERF_HOST = "perf.host"; + private static final String PERF_SERVER_MSG_SIZE = "perf.server.msg_size"; // Default property values private static final String DEFAULT_HOST = "localhost"; @@ -44,6 +45,7 @@ public class PerfConfig private static final int DEFAULT_CLIENT_NUM_THREADS = 100; private static final int DEFAULT_CLIENT_NUM_MSGS = 500 * 1000; private static final int DEFAULT_CLIENT_MSG_SIZE = 1000; + private static final int DEFAULT_SERVER_MSG_SIZE = 1000; public static int getHttpPort() { @@ -65,6 +67,11 @@ public static int getMessageSize() return getInt(PERF_CLIENT_MSG_SIZE); } + public static int getServerMessageSize() + { + return getInt(PERF_SERVER_MSG_SIZE); + } + public static URI getRelativeUri() { return getUri(PERF_RELATIVE_URI); diff --git a/r2-int-test/src/test/java/test/r2/perf/client/StringRequestGenerator.java b/r2-perf-test/src/test/java/test/r2/perf/StringGenerator.java similarity index 71% rename from r2-int-test/src/test/java/test/r2/perf/client/StringRequestGenerator.java rename to r2-perf-test/src/test/java/test/r2/perf/StringGenerator.java index bfacd42630..b37da1a460 100644 --- a/r2-int-test/src/test/java/test/r2/perf/client/StringRequestGenerator.java +++ b/r2-perf-test/src/test/java/test/r2/perf/StringGenerator.java @@ -14,20 +14,19 @@ limitations under the License. */ -package test.r2.perf.client; +package test.r2.perf; import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; +import test.r2.perf.Generator; -public class StringRequestGenerator implements RequestGenerator + +public class StringGenerator implements Generator { - private final AtomicInteger _msgCounter; private final String _msg; - public StringRequestGenerator(int numMsgs, int msgSize) + public StringGenerator(int msgSize) { - _msgCounter = new AtomicInteger(numMsgs); - char[] msg = new char[msgSize]; Arrays.fill(msg, 'a'); _msg = new String(msg); @@ -36,12 +35,6 @@ public StringRequestGenerator(int numMsgs, int msgSize) @Override public String nextMessage() { - if (_msgCounter.getAndDecrement() > 0) - { - - return _msg; - } - - return null; + return _msg; } } \ No newline at end of file diff --git a/r2-int-test/src/test/java/test/r2/perf/client/AbstractClientRunnable.java b/r2-perf-test/src/test/java/test/r2/perf/client/AbstractClientRunnable.java similarity index 90% rename from r2-int-test/src/test/java/test/r2/perf/client/AbstractClientRunnable.java rename to r2-perf-test/src/test/java/test/r2/perf/client/AbstractClientRunnable.java index d128db1c53..c76c60d055 100644 --- a/r2-int-test/src/test/java/test/r2/perf/client/AbstractClientRunnable.java +++ b/r2-perf-test/src/test/java/test/r2/perf/client/AbstractClientRunnable.java @@ -22,6 +22,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; +import test.r2.perf.Generator; + /** * @author Chris Pettitt @@ -31,11 +33,11 @@ { private final AtomicReference _stats; private final CountDownLatch _startLatch; - private final RequestGenerator _workGen; + private final Generator _workGen; public AbstractClientRunnable(AtomicReference stats, CountDownLatch startLatch, - RequestGenerator reqGen) + Generator reqGen) { _stats = stats; _startLatch = startLatch; @@ -61,7 +63,7 @@ public void run() { final FutureCallback callback = new FutureCallback(); - long start = System.currentTimeMillis(); + long start = System.nanoTime(); sendMessage(nextMsg, callback); @@ -72,7 +74,7 @@ public void run() try { callback.get(); - long elapsed = System.currentTimeMillis() - start; + long elapsed = System.nanoTime() - start; stats.success(elapsed); } catch (Exception e) diff --git a/r2-int-test/src/test/java/test/r2/perf/client/ClientRunnableFactory.java b/r2-perf-test/src/test/java/test/r2/perf/client/ClientRunnableFactory.java similarity index 100% rename from r2-int-test/src/test/java/test/r2/perf/client/ClientRunnableFactory.java rename to r2-perf-test/src/test/java/test/r2/perf/client/ClientRunnableFactory.java diff --git a/r2-int-test/src/test/java/test/r2/perf/client/PerfClient.java b/r2-perf-test/src/test/java/test/r2/perf/client/PerfClient.java similarity index 98% rename from r2-int-test/src/test/java/test/r2/perf/client/PerfClient.java rename to r2-perf-test/src/test/java/test/r2/perf/client/PerfClient.java index 0ecb4dc099..e3788ac361 100644 --- a/r2-int-test/src/test/java/test/r2/perf/client/PerfClient.java +++ b/r2-perf-test/src/test/java/test/r2/perf/client/PerfClient.java @@ -85,7 +85,7 @@ public void run() } // Reset the stats after the warmup period - statsRef.set(new Stats(System.currentTimeMillis())); + statsRef.set(new Stats(System.currentTimeMillis(), true)); for (Thread worker : workers) { diff --git a/r2-int-test/src/test/java/test/r2/perf/client/PerfClients.java b/r2-perf-test/src/test/java/test/r2/perf/client/PerfClients.java similarity index 55% rename from r2-int-test/src/test/java/test/r2/perf/client/PerfClients.java rename to r2-perf-test/src/test/java/test/r2/perf/client/PerfClients.java index e7a4bc4722..308adcbe60 100644 --- a/r2-int-test/src/test/java/test/r2/perf/client/PerfClients.java +++ b/r2-perf-test/src/test/java/test/r2/perf/client/PerfClients.java @@ -18,14 +18,19 @@ package test.r2.perf.client; +import com.linkedin.common.callback.Callbacks; +import com.linkedin.common.util.None; import com.linkedin.r2.message.rest.RestRequest; import com.linkedin.r2.transport.common.Client; +import com.linkedin.r2.transport.common.TransportClientFactory; import com.linkedin.r2.transport.common.bridge.client.TransportClient; import com.linkedin.r2.transport.common.bridge.client.TransportClientAdapter; import com.linkedin.r2.transport.http.client.HttpClientFactory; import java.net.URI; import java.util.Collections; +import test.r2.perf.Generator; + /** * @author Chris Pettitt @@ -33,13 +38,41 @@ */ public class PerfClients { + private static final TransportClientFactory FACTORY = new HttpClientFactory(); + private static int NUM_CLIENTS = 0; + public static PerfClient httpRest(URI uri, int numThreads, int numMsgs, int msgSize) { - final TransportClient transportClient = new HttpClientFactory().getClient(Collections.emptyMap()); + final TransportClient transportClient = FACTORY.getClient(Collections.emptyMap()); final Client client = new TransportClientAdapter(transportClient); - final RequestGenerator reqGen = new RestRequestGenerator(uri, numMsgs, msgSize); + final Generator reqGen = new RestRequestGenerator(uri, numMsgs, msgSize); final ClientRunnableFactory crf = new RestClientRunnableFactory(client, reqGen); - return new PerfClient(crf, numThreads); + return new FactoryClient(crf, numThreads); + } + + private static class FactoryClient extends PerfClient + { + public FactoryClient(ClientRunnableFactory runnableFactory, int numThreads) + { + super(runnableFactory, numThreads); + synchronized (PerfClients.class) + { + NUM_CLIENTS++; + } + } + + @Override + public void shutdown() + { + super.shutdown(); + synchronized (PerfClients.class) + { + if (--NUM_CLIENTS == 0) + { + FACTORY.shutdown(Callbacks.empty()); + } + } + } } } diff --git a/r2-int-test/src/test/java/test/r2/perf/client/PrintResultsTask.java b/r2-perf-test/src/test/java/test/r2/perf/client/PrintResultsTask.java similarity index 96% rename from r2-int-test/src/test/java/test/r2/perf/client/PrintResultsTask.java rename to r2-perf-test/src/test/java/test/r2/perf/client/PrintResultsTask.java index f34513c2e4..72c235fbe6 100644 --- a/r2-int-test/src/test/java/test/r2/perf/client/PrintResultsTask.java +++ b/r2-perf-test/src/test/java/test/r2/perf/client/PrintResultsTask.java @@ -50,14 +50,14 @@ public void run() System.out.println("-------"); System.out.println(" Total Requests: " + stats.getSentCount()); System.out.println(" Elapsed: " + elapsedTime); - System.out.println(" Mean latency (in millis): " + snapshot.getAverage()); + System.out.println(" Mean latency (in millis): " + snapshot.getAverage() / 10E6); System.out.println(" Reqs / Sec: " + reqPerSec); System.out.println(" Errors: " + stats.getErrorCount()); - System.out.println(" Min latency: " + snapshot.getMinimum()); - System.out.println(" 50% latency: " + snapshot.get50Pct()); - System.out.println(" 90% latency: " + snapshot.get90Pct()); - System.out.println(" 95% latency: " + snapshot.get95Pct()); - System.out.println(" 99% latency: " + snapshot.get99Pct()); - System.out.println(" Max latency: " + snapshot.getMaximum()); + System.out.println(" Min latency: " + snapshot.getMinimum() / 10E6); + System.out.println(" 50% latency: " + snapshot.get50Pct() / 10E6); + System.out.println(" 90% latency: " + snapshot.get90Pct() / 10E6); + System.out.println(" 95% latency: " + snapshot.get95Pct() / 10E6); + System.out.println(" 99% latency: " + snapshot.get99Pct() / 10E6); + System.out.println(" Max latency: " + snapshot.getMaximum() / 10E6); } } diff --git a/r2-int-test/src/test/java/test/r2/perf/client/RestClientRunnable.java b/r2-perf-test/src/test/java/test/r2/perf/client/RestClientRunnable.java similarity index 94% rename from r2-int-test/src/test/java/test/r2/perf/client/RestClientRunnable.java rename to r2-perf-test/src/test/java/test/r2/perf/client/RestClientRunnable.java index 2a96264dc7..a6033e0bde 100644 --- a/r2-int-test/src/test/java/test/r2/perf/client/RestClientRunnable.java +++ b/r2-perf-test/src/test/java/test/r2/perf/client/RestClientRunnable.java @@ -24,6 +24,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; +import test.r2.perf.Generator; + /** * @author Chris Pettitt @@ -36,7 +38,7 @@ public RestClientRunnable(Client client, AtomicReference stats, CountDownLatch startLatch, - RequestGenerator reqGen) + Generator reqGen) { super(stats, startLatch, reqGen); _client = client; diff --git a/r2-int-test/src/test/java/test/r2/perf/client/RestClientRunnableFactory.java b/r2-perf-test/src/test/java/test/r2/perf/client/RestClientRunnableFactory.java similarity index 90% rename from r2-int-test/src/test/java/test/r2/perf/client/RestClientRunnableFactory.java rename to r2-perf-test/src/test/java/test/r2/perf/client/RestClientRunnableFactory.java index 7a7124f211..7816a2f486 100644 --- a/r2-int-test/src/test/java/test/r2/perf/client/RestClientRunnableFactory.java +++ b/r2-perf-test/src/test/java/test/r2/perf/client/RestClientRunnableFactory.java @@ -24,6 +24,8 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; +import test.r2.perf.Generator; + /** * @author Chris Pettitt @@ -32,9 +34,9 @@ public class RestClientRunnableFactory implements ClientRunnableFactory { private final Client _client; - private final RequestGenerator _reqGen; + private final Generator _reqGen; - public RestClientRunnableFactory(Client client, RequestGenerator reqGen) + public RestClientRunnableFactory(Client client, Generator reqGen) { _client = client; _reqGen = reqGen; diff --git a/r2-int-test/src/test/java/test/r2/perf/client/RestRequestGenerator.java b/r2-perf-test/src/test/java/test/r2/perf/client/RestRequestGenerator.java similarity index 60% rename from r2-int-test/src/test/java/test/r2/perf/client/RestRequestGenerator.java rename to r2-perf-test/src/test/java/test/r2/perf/client/RestRequestGenerator.java index 0e54c57584..8dbd5fb473 100644 --- a/r2-int-test/src/test/java/test/r2/perf/client/RestRequestGenerator.java +++ b/r2-perf-test/src/test/java/test/r2/perf/client/RestRequestGenerator.java @@ -21,39 +21,49 @@ import com.linkedin.r2.message.rest.RestRequestBuilder; import java.net.URI; +import java.util.concurrent.atomic.AtomicInteger; +import test.r2.perf.Generator; +import test.r2.perf.StringGenerator; + /** * @author Chris Pettitt * @version $Revision$ */ -public class RestRequestGenerator implements RequestGenerator +public class RestRequestGenerator implements Generator { private final URI _uri; - private final StringRequestGenerator _generator; + private final StringGenerator _generator; + private final AtomicInteger _msgCounter; + public RestRequestGenerator(URI uri, int numMsgs, int msgSize) { - this(uri, new StringRequestGenerator(numMsgs, msgSize)); + this(uri, numMsgs, new StringGenerator(msgSize)); } - public RestRequestGenerator(URI uri, StringRequestGenerator generator) + public RestRequestGenerator(URI uri, int numMsgs, StringGenerator generator) { _uri = uri; _generator = generator; + _msgCounter = new AtomicInteger(numMsgs); } @Override public RestRequest nextMessage() { - final String stringMsg = _generator.nextMessage(); - if (stringMsg == null) + if (_msgCounter.getAndDecrement() > 0) + { + final String stringMsg = _generator.nextMessage(); + + return new RestRequestBuilder(_uri) + .setEntity(stringMsg.getBytes()) + .setMethod("POST") + .build(); + } + else { return null; } - - return new RestRequestBuilder(_uri) - .setEntity(stringMsg.getBytes()) - .setMethod("POST") - .build(); } } diff --git a/r2-int-test/src/test/java/test/r2/perf/client/Stats.java b/r2-perf-test/src/test/java/test/r2/perf/client/Stats.java similarity index 81% rename from r2-int-test/src/test/java/test/r2/perf/client/Stats.java rename to r2-perf-test/src/test/java/test/r2/perf/client/Stats.java index 44d8fc5f59..8b4bd6f0cd 100644 --- a/r2-int-test/src/test/java/test/r2/perf/client/Stats.java +++ b/r2-perf-test/src/test/java/test/r2/perf/client/Stats.java @@ -24,6 +24,9 @@ import com.linkedin.common.stats.LongTracking; import java.util.concurrent.atomic.AtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * @author Steven Ihde @@ -32,7 +35,10 @@ public class Stats { + private static final Logger LOG = LoggerFactory.getLogger(Stats.class); + private final long _startTime; + private final boolean _logEnabled; private final AtomicLong _sent = new AtomicLong(); private final AtomicLong _success = new AtomicLong(); private final AtomicLong _error = new AtomicLong(); @@ -41,8 +47,14 @@ public class Stats private final LongTracking _latencyTracker = new LongTracking(); public Stats(long startTime) + { + this(startTime, false); + } + + public Stats(long startTime, boolean logEnabled) { _startTime = startTime; + _logEnabled = logEnabled; } public void sent() @@ -56,12 +68,20 @@ public void success(long elapsedTime) { _latencyTracker.addValue(elapsedTime); } + if (_logEnabled) + { + LOG.info("Success, {}, NA", elapsedTime); + } } public void error(Exception error) { _error.incrementAndGet(); _lastError = error; + if (_logEnabled) + { + LOG.info("Error, NA, {}", error.toString()); + } } public long getElapsedTime() diff --git a/r2-int-test/src/test/java/test/r2/perf/driver/RunHttpRestClient.java b/r2-perf-test/src/test/java/test/r2/perf/driver/RunHttpRestClient.java similarity index 100% rename from r2-int-test/src/test/java/test/r2/perf/driver/RunHttpRestClient.java rename to r2-perf-test/src/test/java/test/r2/perf/driver/RunHttpRestClient.java diff --git a/r2-int-test/src/test/java/test/r2/perf/driver/RunHttpServer.java b/r2-perf-test/src/test/java/test/r2/perf/driver/RunHttpServer.java similarity index 79% rename from r2-int-test/src/test/java/test/r2/perf/driver/RunHttpServer.java rename to r2-perf-test/src/test/java/test/r2/perf/driver/RunHttpServer.java index 982c557b2c..834d4850a4 100644 --- a/r2-int-test/src/test/java/test/r2/perf/driver/RunHttpServer.java +++ b/r2-perf-test/src/test/java/test/r2/perf/driver/RunHttpServer.java @@ -24,18 +24,27 @@ import java.io.IOException; import java.net.URI; + /** * @author Chris Pettitt * @version $Revision$ */ public class RunHttpServer { + private static volatile Server SERVER; + public static void main(String[] args) throws IOException { final int port = PerfConfig.getHttpPort(); final URI relativeUri = PerfConfig.getRelativeUri(); + final int msgSize = PerfConfig.getServerMessageSize(); - final Server server = new HttpPerfServerFactory().create(port, relativeUri); - server.start(); + SERVER = new HttpPerfServerFactory().create(port, relativeUri, msgSize); + SERVER.start(); + } + + public static void stop() throws IOException + { + SERVER.stop(); } } diff --git a/r2-int-test/src/test/java/test/r2/perf/server/AbstractPerfServerFactory.java b/r2-perf-test/src/test/java/test/r2/perf/server/AbstractPerfServerFactory.java similarity index 59% rename from r2-int-test/src/test/java/test/r2/perf/server/AbstractPerfServerFactory.java rename to r2-perf-test/src/test/java/test/r2/perf/server/AbstractPerfServerFactory.java index d1b81baae9..4ff33ebd28 100644 --- a/r2-int-test/src/test/java/test/r2/perf/server/AbstractPerfServerFactory.java +++ b/r2-perf-test/src/test/java/test/r2/perf/server/AbstractPerfServerFactory.java @@ -17,13 +17,22 @@ /* $Id$ */ package test.r2.perf.server; +import com.linkedin.common.callback.Callback; +import com.linkedin.r2.message.RequestContext; +import com.linkedin.r2.message.rest.RestRequest; +import com.linkedin.r2.message.rest.RestResponse; +import com.linkedin.r2.sample.echo.EchoService; import com.linkedin.r2.sample.echo.EchoServiceImpl; import com.linkedin.r2.sample.echo.rest.RestEchoServer; +import com.linkedin.r2.transport.common.RestRequestHandler; import com.linkedin.r2.transport.common.Server; import com.linkedin.r2.transport.common.bridge.server.TransportDispatcher; import com.linkedin.r2.transport.common.bridge.server.TransportDispatcherBuilder; import java.net.URI; +import test.r2.perf.Generator; +import test.r2.perf.StringGenerator; + /** * @author Chris Pettitt @@ -31,14 +40,31 @@ */ public abstract class AbstractPerfServerFactory { - public Server create(int port, URI echoUri) + public Server create(int port, URI echoUri, int msg_size) { + final TransportDispatcher dispatcher = new TransportDispatcherBuilder() - .addRestHandler(echoUri, new RestEchoServer(new EchoServiceImpl())) + .addRestHandler(echoUri, new RestEchoServer(new PerfServiceImpl(msg_size))) .build(); return createServer(port, dispatcher); } protected abstract Server createServer(int port, TransportDispatcher dispatcher); + + public class PerfServiceImpl implements EchoService + { + private final Generator _stringGenerator; + + public PerfServiceImpl(int msg_size) + { + _stringGenerator = new StringGenerator(msg_size); + } + + @Override + public void echo(String msg, Callback callback) + { + callback.onSuccess(_stringGenerator.nextMessage()); + } + } } diff --git a/r2-int-test/src/test/java/test/r2/perf/server/HttpPerfServerFactory.java b/r2-perf-test/src/test/java/test/r2/perf/server/HttpPerfServerFactory.java similarity index 100% rename from r2-int-test/src/test/java/test/r2/perf/server/HttpPerfServerFactory.java rename to r2-perf-test/src/test/java/test/r2/perf/server/HttpPerfServerFactory.java diff --git a/r2-perf-test/src/test/resources/log4j2.xml b/r2-perf-test/src/test/resources/log4j2.xml new file mode 100644 index 0000000000..7c84c8e171 --- /dev/null +++ b/r2-perf-test/src/test/resources/log4j2.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file