You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LoadTestingWithGatling.asciidoc 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. ---
  2. title: Load Testing With Gatling
  3. order: 51
  4. layout: page
  5. ---
  6. [[loading-testing-with-gatling]]
  7. = Load testing with Gatling
  8. http://gatling.io[Gatling] is a powerful tool for load testing. Compared
  9. to WebDriver/Selenium/TestBench, it doesn't render the actual content,
  10. but just simulates messages clients send to the server. The server
  11. doesn't know if the test tool actually does something with the
  12. responses, so it gives you perfectly valid numbers for your applications
  13. performance - on the server side. It scales very well, so you don' t
  14. need huge army of nodes to bombard your application under test. It can
  15. be used with Vaadin as such, but with these tips you hopefully get
  16. started easier.
  17. [[vaadin-tips-to-make-tests-more-stable-and-easier-to-create]]
  18. Vaadin tips to make tests more stable and easier to create
  19. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  20. Gatling works with Vaadin "out of the box", but there are some obstacles
  21. you might face if you don't understand a bit how Vaadin communication
  22. works. E.g. plain recordings from standard Vaadin apps will not work
  23. properly.
  24. The communication that Vaadin's "thin client" in browser does with the
  25. server side has couple of checks that it does to improve robustness and
  26. security. One can simulate these with Gatling as well, by e.g.
  27. https://github.com/mstahv/v-quiz/blob/master/src/test/scala/loadtest/WebSocketVaadinSimulation.scala#L84[reading the XSRF prevention key into a variable] and passing the value
  28. https://github.com/mstahv/v-quiz/blob/master/src/test/scala/loadtest/WebSocketVaadinSimulation.scala#L95[in
  29. each response]. However, these setting can be disabled during load
  30. testing to make it easier to write and maintain your application. The
  31. effect for the scalability, when disabling or configuring these, should
  32. be negligible. Feel free to do these, but also remember to remove these
  33. "hacks" when building your production war file. Consider e.g. using
  34. separate maven profile and inject different parameters with it.
  35. [[disabling-xsrf-presentation-key]]
  36. Disabling XSRF presentation key
  37. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  38. The XSRF prevention can be disabled with following servlet parameter
  39. (or similar servlet 3 style parameter). NOTE, do not leave this for
  40. public apps in production.
  41. [source,xml]
  42. ....
  43. <context-param>
  44. <param-name>disable-xsrf-protection</param-name>
  45. <param-value>true</param-value>
  46. </context-param>
  47. ....
  48. [[disabling-syncid-happens-with-similar-parameter]]
  49. Disabling syncId happens with similar parameter
  50. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  51. [source,xml]
  52. ....
  53. <context-param>
  54. <param-name>syncIdCheck</param-name>
  55. <param-value>false</param-value>
  56. </context-param>
  57. ....
  58. If you want to do the above with Java Servlet 3.0 annotations, use the
  59. following:
  60. [source,java]
  61. ....
  62. initParams = {
  63. @WebInitParam(name = "disable-xsrf-protection", value = "true"),
  64. @WebInitParam(name = "syncIdCheck", value = "false")}
  65. ....
  66. [[using-debug-ids-in-communication]]
  67. Using debug ids in communication
  68. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  69. If you want to minimize the effort needed for maintaining your
  70. scalability tests, you probably want to do a small hack to the Vaadin
  71. communication mechanism. Normally Vaadin uses a incrementing session
  72. wide identifier to connect components to their "client side
  73. counterparts". Thus, if you add a one single component to your login
  74. screen, the whole load test simulation might be broken.
  75. You can set "id" for each component, but in recent Vaadin versions this
  76. id is no more used in the communication, but only assigned to
  77. client dom. This can still be enforced with a specially crafted
  78. extension to VaadinServlet. An implementation for the "competing tool" JMeter can be
  79. found at <<jmeter-vaadin-servlet-extension>>. This implementation works for Gatling users
  80. as well. Note that, it is suggested to do this only for load testing, and NOT
  81. for the production.
  82. An alternative way to handle these ids is to use a similar mechanism as in case of
  83. XSRF prevention keys. For instance, you can try to extract id of a button by finding
  84. it from some of the previous responses based on its caption or css id. And, then use
  85. hand crafter regular expression to extract it from the response. As you might have
  86. guessed this is tedious if there are a lot of components interacted. But, benefit of
  87. this approach is that you do not have to do code changes to your application just
  88. for the testing purpose.
  89. [[ignoring-obsolete-static-file-requests]]
  90. Ignoring "obsolete" static file requests
  91. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  92. One of the most simplest and cheapest method to improve your apps
  93. scalability is to serve static files form a separate server or from a
  94. CDN provider. Thus it might make sense to leave loading those files away
  95. from your test script. If you do the script manually, just don't add
  96. requests for static files (js/css/images/...). If you recorded your test
  97. case, just remove these form the script. Check out the example project
  98. that only uses the required files.
  99. [[testing-with-websockets]]
  100. Testing with WebSockets
  101. ~~~~~~~~~~~~~~~~~~~~~~~
  102. If you want to load test your application with the most advanced
  103. communication channel, WebSockets, you can do that with Gatling as well.
  104. Using the recorder in this case doesn't work, but handcrafting the test
  105. case isn't that hard once you get started. The example app has a branch
  106. with WebSocket test case. With WebSocket communication it might also be
  107. handy to disable xsrf prevention and the so called "syncid".
  108. First two request are just normal http requests. The first gets the
  109. "host page" and also the initial state request is done with normal XHR.
  110. The difference to normal Vaadin communication is that it is to be sent
  111. to "/PUSH" address.
  112. After the initial state request you start to use the special WebSocket
  113. API in Gatling. There are lot of things to keep in mind with this
  114. fundamentally totally different kind of communication mechanism. Check
  115. out Gatling's generic websocket help for basic info.
  116. When you start handcrafting the WebSocket simulation, the easiest tool
  117. is probably Chrome's dev tools. With that you can open normal browser
  118. session and "sniff" the traffic that is sent to the server and also the
  119. messages that are received. An easy option is just to copy paste the
  120. payloads and possibly add some verification to ensure proper answers are
  121. received. The websocket example is built with special variable to work
  122. without disabling xsrf verification.
  123. If you are using random input in your load tests, something that is
  124. highly suggested for realistic numbers, you might end up in small
  125. problems. The message format, by Atmosphere, has a weird number and "|"
  126. in front of each message. That number tells the message length and it
  127. must really match the real message length. Create a simple helper
  128. function to calculate that if your input data length varies.
  129. [source,javascript]
  130. ....
  131. import io.gatling.core.session._
  132. import io.gatling.core.session.el._
  133. def atmoMessage(message: Expression[String]) = message.map(m => m.length + '|' + m)
  134. .sendText(atmoMessage("SomeMessage"))
  135. ....
  136. If (and when) you probably want to close the websocket connection
  137. cleanly, you need to notify the server with an extra xhr with a
  138. identifier given by the atmosphere framework. The key is the first
  139. message that the server sends when you connect to it. 
  140. Check out this script for
  141. https://github.com/mstahv/v-quiz/blob/master/src/test/scala/loadtest/WebSocketVaadinSimulation.scala[an
  142. example using WebSocket] communication. It also saves XSRF prevention
  143. key to variable, so it don't need it to be disabled from the server.
  144. [[configuring-gatling-to-the-web-app-build]]
  145. Configuring Gatling to the Web app build
  146. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  147. It is a good habit to keep your tests in the same projects as your
  148. actual application. Then you can easily verify your application still
  149. scales, after you have for example written a cryptic SQL query.
  150. Even better if you can make a Gatling script to be executed during
  151. builds to make. Gatling has http://gatling.io/docs/current/extensions/maven_plugin/[a
  152. maven plugin] that can do exactly this thing.
  153. https://github.com/mstahv/gatling-vaadin-example[The example project
  154. setup] executes a test during basic "mvn install". With similar setup in
  155. a real project, your CI server most likely saves results stored under
  156. target directory. This way it is easy to check it out afterwards how the
  157. performance of your application has evolved during its development.
  158. [[jmeter-vaadin-servlet-extension]]
  159. JMeter Vaadin Servlet extension
  160. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  161. The implementation referred to in <<using-debug-ids-in-communication>>
  162. [source,java]
  163. ....
  164. package com.example.vaadin7jmeterservlet;
  165. import com.vaadin.server.ClientConnector;
  166. import com.vaadin.server.DeploymentConfiguration;
  167. import com.vaadin.server.ServiceException;
  168. import com.vaadin.server.VaadinRequest;
  169. import com.vaadin.server.VaadinService;
  170. import com.vaadin.server.VaadinServlet;
  171. import com.vaadin.server.VaadinServletService;
  172. import com.vaadin.server.VaadinSession;
  173. import com.vaadin.ui.Component;
  174. /**
  175. * @author Marcus Hellberg (marcus@vaadin.com)
  176. * Further modified by Johannes Tuikkala (johannes@vaadin.com)
  177. */
  178. public class JMeterServlet extends VaadinServlet {
  179. private static final long serialVersionUID = 898354532369443197L;
  180. public JMeterServlet() {
  181. System.setProperty(getPackageName() + "." + "disable-xsrf-protection",
  182. "true");
  183. }
  184. @Override
  185. protected VaadinServletService createServletService(
  186. DeploymentConfiguration deploymentConfiguration)
  187. throws ServiceException {
  188. JMeterService service = new JMeterService(this, deploymentConfiguration);
  189. service.init();
  190. return service;
  191. }
  192. private String getPackageName() {
  193. String pkgName;
  194. final Package pkg = this.getClass().getPackage();
  195. if (pkg != null) {
  196. pkgName = pkg.getName();
  197. } else {
  198. final String className = this.getClass().getName();
  199. pkgName = new String(className.toCharArray(), 0,
  200. className.lastIndexOf('.'));
  201. }
  202. return pkgName;
  203. }
  204. public static class JMeterService extends VaadinServletService {
  205. private static final long serialVersionUID = -5874716650679865909L;
  206. public JMeterService(VaadinServlet servlet,
  207. DeploymentConfiguration deploymentConfiguration)
  208. throws ServiceException {
  209. super(servlet, deploymentConfiguration);
  210. }
  211. @Override
  212. protected VaadinSession createVaadinSession(VaadinRequest request)
  213. throws ServiceException {
  214. return new JMeterSession(this);
  215. }
  216. }
  217. public static class JMeterSession extends VaadinSession {
  218. private static final long serialVersionUID = 4596901275146146127L;
  219. public JMeterSession(VaadinService service) {
  220. super(service);
  221. }
  222. @Override
  223. public String createConnectorId(ClientConnector connector) {
  224. if (connector instanceof Component) {
  225. Component component = (Component) connector;
  226. return component.getId() == null ? super
  227. .createConnectorId(connector) : component.getId();
  228. }
  229. return super.createConnectorId(connector);
  230. }
  231. }
  232. }
  233. ....