- HtmlUnit provides a compact Web client. With little programming you can emulate some user action. An example follows which logs a new user.
- Start the Application Server in a separate JVM. Allow for remote JMX Monitoring.
- Start thousands of new sessions en follow what happens to memory usage in the server JVM using MemoryMXBean. JConsole is the easiest way to monitor the memory usage, but if you want to do some calculations or logging you'll have to gain access to the remote MXBean programmatically.
The method is independent of any server technology. You don't even need the application sources. A pic to illustrate what is going on:
and some implementation details:
Start the server and allow for remote JMX monitoring
Add the following to the java arguments:
-Dcom.sun.management.jmxremote.port=1444
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
Program a simple web client to perform some user action
HtmlUnit's Webclient is a beauty. Here's an example of logging in in the Pluto portlet container an clicking on a link:
public void loginAndStartUseCase() throws Exception {
final WebClient webClient = new WebClient();
HtmlPage page = (HtmlPage) webClient.getPage("http://localhost:8080/pluto/portal/MyPage");
Listforms = page.getForms();
boolean loginFormSubmitted = false;
for (HtmlForm htmlForm : forms) {
if (htmlForm.getActionAttribute().equals("j_security_check")) {
htmlForm.getInputByName("j_username").setValueAttribute("pluto");
htmlForm.getInputByName("j_password").setValueAttribute("pluto");
htmlForm.submit(htmlForm.getInputByName("login"));
loginFormSubmitted = true;
break;
}
}
if (!loginFormSubmitted) {
throw new RuntimeException("Login Form expected here");
}
page = (HtmlPage) webClient.getPage("http://localhost:8080/pluto/portal/MyPage");
page = clickLink(page, "MyUseCase");
page.getAllHtmlChildElements();
}
private static HtmlPage clickLink(HtmlPage page, String linkText) throws IOException {
LOG.debug("click " + linkText);
Listanchors = page.getAnchors();
for (HtmlAnchor htmlAnchor : anchors) {
if (htmlAnchor.getFirstChild().asText().equals(linkText)) {
return (HtmlPage) htmlAnchor.click();
}
}
throw new RuntimeException("No link " + linkText);
}
Obtain a remote memory monitoring MXBean proxy
private static Object getMXBean(String beanType, Class beanClass, int port) throws MalformedURLException,
IOException, MalformedObjectNameException {
JMXServiceURL u = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + port + "/jmxrmi");
JMXConnector jmxConnector = JMXConnectorFactory.connect(u);
MBeanServerConnection beanServerConnection = jmxConnector.getMBeanServerConnection();
Hashtableprops = new Hashtable ();
props.put("type", beanType);
ObjectName on = new ObjectName("java.lang", props);
Setfound = beanServerConnection.queryMBeans(on, null);
if (found.size() != 1) {
throw new RuntimeException("1 bean expected");
}
ObjectInstance objectInstance = found.iterator().next();
Object mxBean = ManagementFactory.newPlatformMXBeanProxy(beanServerConnection, objectInstance.getObjectName()
.toString(), beanClass);
return mxBean;
}
Start measuring!
Now you can start a new user session (or 10, or 1000), measure the memory impact and calculate whatever you want
MemoryMXBean memoryBean = (MemoryMXBean) getMXBean("Memory", MemoryMXBean.class, 1444);
for (many times) {
for(many_times) { loginAndStartUseCase(); }
memoryBean.gc();
long used = memoryBean.getHeapMemoryUsage().getUsed();
. . .
That's what my memory usage looks like after 10000 sessions:
This is an Excel chart built from comma-separated logging output.
The blue line shows the total memory usage - it usage grows consistently, it looks like no sessions have been released. The red line shows the calculated average session size. The average is pretty unreliable in the begin but stabilizes after a while - the cost of a new user just logging in and starting the use case seems to be around 3k.
Few remarks:
- Programming the Html WebClient for a more complex use case can be time consuming. Is there a good scripting alternative? Selenium was great, but starting a real browser for each user new session is not an option
- Make sure no sessions are released during the measurement. The test goes pretty fast, a normal session timeout of 15-30 min will be more than enough to reach your users limit.
Glad to hear that you've found HtmlUnit to be useful. Feel free to submit suggestions to the HtmlUnit team for making the API less verbose, or if you know (or want to learn) Ruby, you might be able to use Celerity, which is a JRuby wrapper around HtmlUnit which provides a Watir-like API. Take care!
ReplyDeleteThanks for the comment, I didn't know about Watir.
ReplyDeleteThe point is, I don't use HtmlUnit here for tests, but as a *minimal* GUI-less browser to drive the application to a certain state, not to test it. I am not interested in the application output.
Watir doesn't sound like an interesting option in the scenario when I want to start 10,000 sessions, even if it was a better option than Selenium for some reason.
That's not the first time I use HtmlUnit for something else than testing - it is great for Html scraping. A beautiful minimal GUI-less browser.