On Sun, Dec 27, 2020 at 4:16 PM Romain Manni-Bucau <rmannibu...@gmail.com>
wrote:

> Hi everyone,
>
> wonder if there is some work planned in tomcat 10 to 1. makes it start
> faster and 2. make jmx optional.
>
> I did some tests and locally a tomcat start is about 400ms.
> I was surprised to see that Registry.disableRegistry() was taking already
> 65ms whereas it should be almost nothing so refactored the code, added a
> RegistryFactory and really noop impl (not even a mock) and went down to
> 1ms, way better right? except the remaining 64ms went to the next line (new
> StandardHost()). After some investigation time is mainly classloading time
> (in all senses).
>
> So I wonder if it wouldnt make sense to optimize tomcat startup time +
> finally make JMX optional since it is no more used by users in most cases -
> gues we must keep it as an optional plugin since it is used "historically".
>
> The proposal would be to:
>
> 1. reduce classloading tree as much as possible for default case - here
> having jmx optional will help a lot
> 2. probably generate resource bundle as .java at build time to avoid all
> the classloading resource bundle implies (it is like 74 loadClass + as much
> getResource for a simple startup and all loadclass are misses)
> 2.bis. probably add a mode where StringManager uses the default bundle
> without passing through the resource bundle layer
> 3. cut dead code when we know it is inactive (typically the case for jmx)
> 4. open point: bypassing Context: in "main" mode (as the code shared
> after), you just want to bind some logic in a servlet/filter/valve, you
> don't always care about handling contexts (or a single one) so wonder if we
> shouldnt evaluate the fact to add the user logic in a valve for this kind
> of bench and maybe document this case (spring boot could inherit from it
> since it almost never use anything else that default spring servlet and
> binds everything in it).
>
> Here is the kind of case I'd like to see insanely fast (<100ms - and it is
> possible regarding what it does):
>
> public class Main {
>     public static void main(String[] args) throws LifecycleException,
> InterruptedException {
>         final long start = System.nanoTime();
>         Registry.disableRegistry();
>         final long startDisableRegistry = System.nanoTime();
>
>         final Host host = new StandardHost();
>         host.setName("localhost");
>         final long startHost = System.nanoTime();
>
>         final Engine engine = new StandardEngine();
>         engine.setName("Tomcat");
>         engine.setDefaultHost("localhost");
>         engine.setRealm(new NullRealm());
>         engine.addChild(host);
>         final long startEngine = System.nanoTime();
>
>         final var protocolHandler = new Http11Nio2Protocol();
>         protocolHandler.setPort(8080);
>         final long startHttp11Nio2Protocol = System.nanoTime();
>
>         final Service service = new StandardService();
>         service.setName("Tomcat");
>         service.setContainer(engine);
>         service.addConnector(new Connector(protocolHandler));
>         final long startService = System.nanoTime();
>
>         final StandardServer server = new StandardServer();
>         server.setPort(-1);
>         server.addService(service);
>         final long startServer = System.nanoTime();
>
>         final StandardContext context = new StandardContext();
>         context.setUseNaming(false);
>         context.setClearReferencesStopThreads(false);
>         context.setClearReferencesStopTimerThreads(false);
>         context.setClearReferencesHttpClientKeepAliveThread(false);
>         context.setClearReferencesRmiTargets(false);
>         context.setClearReferencesThreadLocals(false);
>         context.setClearReferencesObjectStreamClassCaches(false);
>         context.setWorkDir(System.getProperty("java.io.tmpdir"));
>         context.setName("");
>         context.setPath("");
>         context.setDocBase(null);
>         context.addLifecycleListener(event -> {
>             if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
>                 context.setConfigured(true);
>                 if (context.getLoginConfig() == null) {
>                     context.setLoginConfig(new LoginConfig("NONE", null,
> null, null));
>                     context.getPipeline().addValve(new
> NonLoginAuthenticator());
>                 }
>             }
>         });
>         context.addServletContainerInitializer((classes, ctx) -> {
>             ctx.addServlet("default", new DefaultServlet())
>                     .addMapping("/");
>         }, Set.of());
>         final long startContext = System.nanoTime();
>
>         host.addChild(context);
>
>         server.init();
>         final long inited = System.nanoTime();
>         server.start();
>         final long started = System.nanoTime();
>         // new CountDownLatch(1).await();
>
>         final var contextClassLoader =
> Thread.currentThread().getContextClassLoader();
>
>         server.stop();
>         server.destroy();
>         final long stopped = System.nanoTime();
>
>         System.out.println(TimeUnit.NANOSECONDS.toMillis(started - start) +
> "ms");
>         System.out.println(TimeUnit.NANOSECONDS.toMillis(stopped - started)
> + "ms");
>         System.out.println();
>         System.out.println("startDisableRegistry:    " +
> TimeUnit.NANOSECONDS.toMillis(startDisableRegistry - start) + "ms");
>         System.out.println("startHost:               " +
> TimeUnit.NANOSECONDS.toMillis(startHost - startDisableRegistry) + "ms");
>         System.out.println("startEngine:             " +
> TimeUnit.NANOSECONDS.toMillis(startEngine - startHost) + "ms");
>         System.out.println("startHttp11Nio2Protocol: " +
> TimeUnit.NANOSECONDS.toMillis(startHttp11Nio2Protocol - startEngine) +
> "ms");
>         System.out.println("startService:            " +
> TimeUnit.NANOSECONDS.toMillis(startService - startEngine) + "ms");
>         System.out.println("startServer:             " +
> TimeUnit.NANOSECONDS.toMillis(startServer - startService) + "ms");
>         System.out.println("startContext:            " +
> TimeUnit.NANOSECONDS.toMillis(startContext - startServer) + "ms");
>         System.out.println("actualInit:              " +
> TimeUnit.NANOSECONDS.toMillis(inited - startContext) + "ms");
>         System.out.println("actualStart:             " +
> TimeUnit.NANOSECONDS.toMillis(started - inited) + "ms");
>     }
> }
>
>
> Current reference output (medium values) is - on java 11:
>
> 364ms
> 22ms
>
> startDisableRegistry:    58ms
> startHost:               19ms
> startEngine:             7ms
> startHttp11Nio2Protocol: 25ms
> startService:            39ms
> startServer:             12ms
> startContext:            26ms
> actualStart:             200ms
>
> Wdyt? Anyone had a look before?
>

It is not possible to disable JMX by default since in addition to the
obvious monitoring with visualvm or similar, it is also a building block
for a lot of other features in Tomcat (manager and status webapps, TLS
reload, storeconfig). It's a bit the same for JNDI.

Overall, AOT compilation is likely a better solution for the use case you
describe, that was the whole point of adding support for it.

Rémy


>
>
> PS: if curious the registry patch i did was:
> https://gist.github.com/rmannibucau/e7cb9674fa4a21ccfe9eb956b509fad1 -
> indeed since it does not help overall I don't ask to integrate it at all,
> just a FYI
>
> Romain Manni-Bucau
> @rmannibucau <https://twitter.com/rmannibucau> |  Blog
> <https://rmannibucau.metawerx.net/> | Old Blog
> <http://rmannibucau.wordpress.com> | Github <
> https://github.com/rmannibucau> |
> LinkedIn <https://www.linkedin.com/in/rmannibucau> | Book
> <
> https://www.packtpub.com/application-development/java-ee-8-high-performance
> >
>

Reply via email to