/*
* 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 org.esigate.test.cases;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletResponse;
import junit.framework.TestCase;
import junitx.framework.AssertionFailedError;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.esigate.http.HttpResponseUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Performance test
*
* @author Alexis Thaveau
*/
public class PerformanceTestCase extends TestCase {
/**
* Runnable that perform a GET request. This object is reusable and thread safe.
*/
private class HttpGetRequestRunnable implements Runnable {
long count = 0;
Throwable exception;
String lastResult;
final String url;
private HttpGetRequestRunnable(String url) {
this.url = url;
}
public void run() {
if (exception == null) {
count++;
try {
HttpUriRequest request = new HttpGet(url);
HttpResponse response = httpClient.execute(request);
String result = HttpResponseUtils.toString(response, null);
if (lastResult == null) {
lastResult = result;
}
assertEquals("Status should be 200", HttpServletResponse.SC_OK, response.getStatusLine()
.getStatusCode());
assertEquals("Result should always be the same", lastResult, result);
} catch (Throwable e) {
exception = e;
}
}
}
}
private final static String AGGREGATED1 = "http://localhost:8080/esigate-app-aggregated1/";
private final static String AGGREGATED2 = "http://localhost:8080/esigate-app-aggregated2/";
private final static String AGGREGATOR = "http://localhost:8080/esigate-app-aggregator/";
private final static String AGGREGATOR_NO_CACHE = "http://localhost:8080/esigate-app-aggregator/nocache/ag1/";
private static final Logger LOG = LoggerFactory.getLogger(PerformanceTestCase.class);
private HttpClient httpClient;
/**
* Execute la tache avec plusieurs Threads
*
* @param request
* @return
* @throws Exception
*/
private long execute(HttpGetRequestRunnable request, int numberOfRequests, int threads) throws Exception {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
httpClient =
HttpClientBuilder
.create()
.setConnectionManager(connectionManager)
.setMaxConnTotal(threads)
.setMaxConnPerRoute(threads)
.setDefaultRequestConfig(
RequestConfig.custom().setConnectTimeout(10000).setSocketTimeout(10000).build())
.build();
// Warm up
request.run();
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(threads, threads, 5, TimeUnit.SECONDS, queue);
long start = System.currentTimeMillis();
threadPool.prestartAllCoreThreads();
for (int i = 0; i < numberOfRequests; i++) {
threadPool.submit(request);
}
threadPool.shutdown();
// wait maximum 20 s
threadPool.awaitTermination(200, TimeUnit.SECONDS);
connectionManager.shutdown();
if (request.exception != null) {
throw new AssertionFailedError("Exception for request " + request.url + " after " + request.count
+ " requests", request.exception);
}
if (threadPool.getCompletedTaskCount() < threadPool.getTaskCount()) {
// All task were not executed
String msg =
request.url + " : Only " + threadPool.getCompletedTaskCount() + "/" + threadPool.getTaskCount()
+ " have been renderered " + " => Maybe a performance issue";
threadPool.shutdownNow();
fail(msg);
}
long end = System.currentTimeMillis();
long execTime = end - start;
LOG.debug("Executed request " + request.url + " " + numberOfRequests + " times with " + threads
+ " threads in " + execTime + "ms");
return execTime;
}
/**
* Performance test
*
* @throws Exception
*/
public void testAggregator() throws Exception {
HttpGetRequestRunnable runAggregated1 = new HttpGetRequestRunnable(AGGREGATED1 + "templateslow.jsp");
HttpGetRequestRunnable runAggregated2 = new HttpGetRequestRunnable(AGGREGATED2 + "template.html");
HttpGetRequestRunnable runAggregator = new HttpGetRequestRunnable(AGGREGATOR + "templateslow.jsp");
HttpGetRequestRunnable runAggregatorNoCache =
new HttpGetRequestRunnable(AGGREGATOR_NO_CACHE + "templateslow.jsp");
long execTimeAggregated1 = execute(runAggregated1, 5000, 50);
long execTimeAggregated2 = execute(runAggregated2, 5000, 50);
long execTimeDirectAccess = execTimeAggregated1 + execTimeAggregated2;
LOG.debug("Total execution time direct access: " + execTimeDirectAccess + "ms");
long execTimeAggregator = execute(runAggregator, 5000, 50);
LOG.debug("Total execution time with aggregator (cache): " + execTimeAggregator + "ms");
long execTimeAggregatorNoCache = execute(runAggregatorNoCache, 5000, 50);
LOG.debug("Total execution time with aggregator (no cache): " + execTimeAggregatorNoCache + "ms");
/*
* Expected time : 10% of total time to retrieve resources without aggregator
*/
long expectedExecTime = Math.round((execTimeDirectAccess) * 1.10);
LOG.debug("Maximum expected :" + expectedExecTime + "ms");
if (execTimeAggregator > expectedExecTime) {
fail("Performance issue :" + execTimeAggregator + "ms to render template.html with aggregator,"
+ execTimeDirectAccess + "ms without aggregator- Expected " + expectedExecTime + "ms");
}
if (execTimeAggregatorNoCache > expectedExecTime) {
fail("Performance issue :" + execTimeAggregatorNoCache
+ "ms to render template.html with aggregator(without cache)," + execTimeDirectAccess
+ "ms without aggregator- Expected " + expectedExecTime + "ms");
}
}
}