Hi all,
I've recently done some performance testing with vertx 2 mainly but I believe from looking at the code for vertx 3 it probably has the same problems:
- JAXB has blocking issues for deserialize and serialize of objects - This NOT a vertx issue but deep down if you use the code JAXB.marshal() or JAXB.unmarshal() the JAXB context is reloaded occasionally, this is because the JAXBContext is stored in a WeakHashMap and so gets garbage collected. There is a synchronized method in the code. The work around is to create you're own JAXBContext and re-use it so no garbage collection (I put them in a ThreadLocal Map for each class).
- The jackson libraries block the event bus thread. I haven't got to the bottom of this entirely, but using a ThreadLocal ObjectMapper improves the number of instances here, This was not in the vertx code but when we used our own ObjectMapper.
- The event bus uses UUID.randomUUID() to generate the reply addresses, this unfortunately uses a static SecureRandom and this does some synchronised stuff around the readBytes call which can block the event loop. I think this was blocked because of lack of entropy (yes vertx is that fast), perhaps the code could be changed so that it uses a deterministic algorithm using Atomic longs or something?
- There was also a blocked thread on the eventloop, but I don't know if this is normal.
Please understand these were happening when the event bus was handling over 5000 messages a second and introduced a small latency of up to 10 ms so not massively critical (except JAXB one which was really problematic).
We used this code (Vertx 2) to monitor the event loops, it only fires every 500 ms so it's not going to catch them all.
vertx.setPeriodic(pollingInterval, timerId -> {
final long time = System.currentTimeMillis();
final Map<Thread,StackTraceElement[]> map = Thread.getAllStackTraces();
map.keySet().stream()
.filter(thread -> thread.getName().startsWith("vert.x-eventloop-thread-"))
.filter(thread -> Thread.State.BLOCKED.equals(thread.getState()))
.forEach(thread -> {
final String message = "Thread [" + thread.getName() + "] is BLOCKED";
final Exception ex = new IllegalStateException(message);
ex.setStackTrace(map.get(thread));
LOGGER.warn(message, ex);
});
LOGGER.trace("Eventloop threads checked. {} millis", System.currentTimeMillis() - time);
});
Examples:
java.lang.IllegalStateException: Thread [vert.x-eventloop-thread-4] is BLOCKED
at java.security.SecureRandom.nextBytes(SecureRandom.java:468) ~[na:1.8.0_77]
at java.util.UUID.randomUUID(UUID.java:145) ~[na:1.8.0_77]
at org.vertx.java.core.eventbus.impl.DefaultEventBus.generateReplyAddress(DefaultEventBus.java:688) ~[vertx-core-2.1.6.jar:n
a]
at org.vertx.java.core.eventbus.impl.DefaultEventBus.sendOrPub(DefaultEventBus.java:706) ~[vertx-core-2.1.6.jar:na]
at org.vertx.java.core.eventbus.impl.DefaultEventBus.sendOrPubWithTimeout(DefaultEventBus.java:674) ~[vertx-core-2.1.6.jar:n
a]
at org.vertx.java.core.eventbus.impl.DefaultEventBus.sendWithTimeout(DefaultEventBus.java:207) ~[vertx-core-2.1.6.jar:na]
Thread [vert.x-eventloop-thread-1] is BLOCKED
java.lang.IllegalStateException: Thread [vert.x-eventloop-thread-1] is BLOCKED
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method) ~[na:1.8.0_77]
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269) ~[na:1.8.0_77]
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93) ~[na:1.8.0_77]
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86) ~[na:1.8.0_77]
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97) ~[na:1.8.0_77]
at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:622) ~[netty-all-4.0.21.Final.jar:4.0.21.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:310) ~[netty-all-4.0.21.Final.jar:4.0.21.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116) ~[netty-all-4.0.21.Final.jar
Thread [vert.x-eventloop-thread-2] is BLOCKED
java.lang.IllegalStateException: Thread [vert.x-eventloop-thread-2] is BLOCKED
at sun.misc.Unsafe.unpark(Native Method) ~[na:1.8.0_77]
at java.util.concurrent.locks.LockSupport.unpark(LockSupport.java:141) ~[na:1.8.0_77]
at java.util.concurrent.locks.AbstractQueuedSynchronizer.unparkSuccessor(AbstractQueuedSynchronizer.java:662) ~[na:1.8.0_77]
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1264) ~[na:1.8.0_77]
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457) ~[na:1.8.0_77]
at java.util.concurrent.LinkedBlockingQueue.signalNotEmpty(LinkedBlockingQueue.java:176) ~[na:1.8.0_77]
at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:430) ~[na:1.8.0_77]
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1361) ~[na:1.8.0_77]
at org.vertx.java.core.impl.OrderedExecutorFactory$OrderedExecutor.execute(OrderedExecutorFactory.java:111) ~[vertx-core-2.1.6.jar:na]
at org.vertx.java.core.impl.DefaultContext.executeOnOrderedWorkerExec(DefaultContext.java:156) ~[vertx-core-2.1.6.jar:na]
at org.vertx.java.core.impl.DefaultVertx.executeBlocking(DefaultVertx.java:402) ~[vertx-core-2.1.6.jar:na]
at org.vertx.java.spi.cluster.impl.hazelcast.HazelcastAsyncMultiMap.get(HazelcastAsyncMultiMap.java:99) ~[vertx-hazelcast-2.1.6.jar:na]
at org.vertx.java.core.eventbus.impl.DefaultEventBus.sendOrPub(DefaultEventBus.java:731) ~[vertx-core-2.1.6.jar:na]
at org.vertx.java.core.eventbus.impl.DefaultEventBus.sendOrPubWithTimeout(DefaultEventBus.java:674) ~[vertx-core-2.1.6.jar:na]
at org.vertx.java.core.eventbus.impl.DefaultEventBus.sendWithTimeout(DefaultEventBus.java:135) ~[vertx-core-2.1.6.jar:na]
at com.odobo.service.achievements.managers.GameAchievementManager.register(GameAchievementManager.java:107) ~[na:na]
at com.odobo.service.achievements.managers.GameAchievementManager.start(GameAchievementManager.java:102) ~[na:na]
at org.vertx.java.platform.impl.DefaultPlatformManager$22.run(DefaultPlatformManager.java:1765) ~[vertx-platform-2.1.6.jar:na]
at org.vertx.java.core.impl.DefaultContext$3.run(DefaultContext.java:175) ~[vertx-core-2.1.6.jar:na]
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:370) ~[netty-all-4.0.21.Final.jar:4.0.21.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:357) ~[netty-all-4.0.21.Final.jar:4.0.21.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116) ~[netty-all-4.0.21.Final.jar:4.0.21.Final]
at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_77]
Peter