title: Workaround for the slowness of the WebClient first request
body: <p>I am using WebClient in a Spring Boot MVC 2.1 project and found that the first request made by the client takes up to 6 seconds. Subsequent requests are way faster (~30ms).</p>
  
  <p>There's a <a href="https://jira.spring.io/browse/SPR-17200" rel="noreferrer">closed issue in Spring's JIRA</a> that advices using Jetty as the WebClient Http connector. I have tried that approach, improving the figures, with a ~800ms first request. This time is an improvement but <strong>it's still far from RestTemplate</strong> which usally takes &lt;200ms.</p>
  
  <p><strong>Netty approach (5s first request):</strong></p>
  
  <p>Conf:</p>
  
  <pre><code>@Bean
  public WebClient webClient() {
      return WebClient.create();
  }
  </code></pre>
  
  <p>Usage:</p>
  
  <pre><code>private final WebClient webClient;
  
  @GetMapping(value="/wc", produces = APPLICATION_JSON_UTF8_VALUE)
  public Mono&lt;String&gt; findWc() throws URISyntaxException {
      URI uri = new URI("http://xxx");
      final Mono&lt;String&gt; response = webClient.get().uri(uri).retrieve().bodyToMono(String.class);
      return response;
  }
  </code></pre>
  
  <p><strong>Jetty approach (800ms first request):</strong></p>
  
  <p>Conf:</p>
  
  <pre><code>@Bean
  public JettyResourceFactory resourceFactory() {
      return new JettyResourceFactory();
  }
  
  @Bean
  public WebClient webClient() {
      ClientHttpConnector connector = new JettyClientHttpConnector(resourceFactory(), null);
      return WebClient.builder().clientConnector(connector).build();
  }
  </code></pre>
  
  <p>Usage: same as before.</p>
  
  <p>There's another "problem" with the Jetty approach. On server shutdown it always produces the following exception:</p>
  
  <pre><code>27-Dec-2018 11:24:20.463 INFO [jetty-http@74305db9-65] org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading Illegal access: this web application instance has been stopped already. Could not load [org.eclipse.jetty.io.ManagedSelector$StopSelector]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
   java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [org.eclipse.jetty.io.ManagedSelector$StopSelector]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
      at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1348)
      at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForClassLoading(WebappClassLoaderBase.java:1336)
      at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1195)
      at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1157)
      at java.lang.Class.getDeclaringClass0(Native Method)
      at java.lang.Class.getDeclaringClass(Class.java:1235)
      at java.lang.Class.getEnclosingClass(Class.java:1277)
      at java.lang.Class.getSimpleBinaryName(Class.java:1443)
      at java.lang.Class.getSimpleName(Class.java:1309)
      at org.eclipse.jetty.io.ManagedSelector$SelectorProducer.toString(ManagedSelector.java:534)
      at java.lang.String.valueOf(String.java:2994)
      at java.lang.StringBuilder.append(StringBuilder.java:131)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.getString(EatWhatYouKill.java:458)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.toStringLocked(EatWhatYouKill.java:447)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.toString(EatWhatYouKill.java:440)
      at org.slf4j.helpers.MessageFormatter.safeObjectAppend(MessageFormatter.java:299)
      at org.slf4j.helpers.MessageFormatter.deeplyAppendParameter(MessageFormatter.java:271)
      at org.slf4j.helpers.MessageFormatter.arrayFormat(MessageFormatter.java:233)
      at org.slf4j.helpers.MessageFormatter.arrayFormat(MessageFormatter.java:173)
      at org.eclipse.jetty.util.log.JettyAwareLogger.log(JettyAwareLogger.java:680)
      at org.eclipse.jetty.util.log.JettyAwareLogger.debug(JettyAwareLogger.java:224)
      at org.eclipse.jetty.util.log.Slf4jLog.debug(Slf4jLog.java:97)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:288)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126)
      at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)
      at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765)
      at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683)
      at java.lang.Thread.run(Thread.java:748)
  
  SLF4J: Failed toString() invocation on an object of type [org.eclipse.jetty.util.thread.strategy.EatWhatYouKill]
  Reported exception:
  java.lang.NoClassDefFoundError: org/eclipse/jetty/io/ManagedSelector$StopSelector
      at java.lang.Class.getDeclaringClass0(Native Method)
      at java.lang.Class.getDeclaringClass(Class.java:1235)
      at java.lang.Class.getEnclosingClass(Class.java:1277)
      at java.lang.Class.getSimpleBinaryName(Class.java:1443)
      at java.lang.Class.getSimpleName(Class.java:1309)
      at org.eclipse.jetty.io.ManagedSelector$SelectorProducer.toString(ManagedSelector.java:534)
      at java.lang.String.valueOf(String.java:2994)
      at java.lang.StringBuilder.append(StringBuilder.java:131)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.getString(EatWhatYouKill.java:458)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.toStringLocked(EatWhatYouKill.java:447)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.toString(EatWhatYouKill.java:440)
      at org.slf4j.helpers.MessageFormatter.safeObjectAppend(MessageFormatter.java:299)
      at org.slf4j.helpers.MessageFormatter.deeplyAppendParameter(MessageFormatter.java:271)
      at org.slf4j.helpers.MessageFormatter.arrayFormat(MessageFormatter.java:233)
      at org.slf4j.helpers.MessageFormatter.arrayFormat(MessageFormatter.java:173)
      at org.eclipse.jetty.util.log.JettyAwareLogger.log(JettyAwareLogger.java:680)
      at org.eclipse.jetty.util.log.JettyAwareLogger.debug(JettyAwareLogger.java:224)
      at org.eclipse.jetty.util.log.Slf4jLog.debug(Slf4jLog.java:97)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:288)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126)
      at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)
      at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765)
      at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683)
      at java.lang.Thread.run(Thread.java:748)
  Caused by: java.lang.ClassNotFoundException: Illegal access: this web application instance has been stopped already. Could not load [org.eclipse.jetty.io.ManagedSelector$StopSelector]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
      at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForClassLoading(WebappClassLoaderBase.java:1338)
      at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1195)
      at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1157)
      ... 25 more
  Caused by: java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [org.eclipse.jetty.io.ManagedSelector$StopSelector]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
      at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1348)
      at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForClassLoading(WebappClassLoaderBase.java:1336)
      ... 27 more
  27-Dec-2018 11:24:20.467 INFO [jetty-http@74305db9-65] org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading Illegal access: this web application instance has been stopped already. Could not load [ch.qos.logback.classic.spi.ThrowableProxy]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
   java.lang.IllegalStateException: Illegal access: this web application instance has been stopped already. Could not load [ch.qos.logback.classic.spi.ThrowableProxy]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access.
      at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForResourceLoading(WebappClassLoaderBase.java:1348)
      at org.apache.catalina.loader.WebappClassLoaderBase.checkStateForClassLoading(WebappClassLoaderBase.java:1336)
      at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1195)
      at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1157)
      at ch.qos.logback.classic.spi.LoggingEvent.&lt;init&gt;(LoggingEvent.java:119)
      at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:419)
      at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:383)
      at ch.qos.logback.classic.Logger.log(Logger.java:765)
      at org.eclipse.jetty.util.log.JettyAwareLogger.log(JettyAwareLogger.java:668)
      at org.eclipse.jetty.util.log.JettyAwareLogger.warn(JettyAwareLogger.java:474)
      at org.eclipse.jetty.util.log.Slf4jLog.warn(Slf4jLog.java:73)
      at org.eclipse.jetty.util.log.Slf4jLog.warn(Slf4jLog.java:67)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.execute(EatWhatYouKill.java:375)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:305)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
      at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126)
      at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)
      at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:765)
      at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:683)
      at java.lang.Thread.run(Thread.java:748)
  
  Exception in thread "jetty-http@74305db9-65" java.lang.NoClassDefFoundError: ch/qos/logback/classic/spi/ThrowableProxy
      at ch.qos.logback.classic.spi.LoggingEvent.&lt;init&gt;(LoggingEvent.java:119)
      at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:419)
      at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:383)
      at ch.qos.logback.classic.Logger.log(Logger.java:765)
      at org.eclipse.jetty.util.log.JettyAwareLogger.log(JettyAwareLogger.java:668)
      at org.eclipse.jetty.util.log.JettyAwareLogger.warn(JettyAwareLogger.java:474)
      at org.eclipse.jetty.util.log.Slf4jLog.warn(Slf4jLog.java:73)
      at org.eclipse.jetty.util.log.Slf4jLog.warn(Slf4jLog.java:67)
      at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:740)
      at java.lang.Thread.run(Thread.java:748)
  </code></pre>
  
  <ol>
  <li><p>How can I avoid this exception?</p></li>
  <li><p>Is there any other way we can use to improve the WebClient first request slowness?</p></li>
  </ol>