tomcat和vert.x服务器nio模型对比

大家知道服务器BIO模式下,每个请求都要创建一个线程,而线程的内存和上下文切换的开销都比较大。所以在BIO模式下,高并发的支持比较差。NIO通过多路复用和eventloop,减少线程开销,从而提高并发量。
vert.x由于其轻量,良好的高并发能力被熟知。
而一些同学会有一些误区,认为tomcat并发能力比vert.x要差。事实上,tomcat从6.x开始,缺省的运行模式就是NIO。从原理上看,并发能力并不差。

Vert.x 服务器nio线程模型

  • 一个acceptor线程,用来监听客户端连接请求;使用selector对监听socket使用多路复用监听;
  • acceptor线程监听到连接请求后,创建一个socket,将socket fd注册到verticle 相应eventloop的selector上;
  • eventloop线程,select各个连接socket的fd,当监听到请求数据时,在当前eventloop线程中执行相应的request handler;
    tomcat和vert.x服务器nio模型对比_第1张图片

以下是vert.x线程和堆栈:
acceptor线程,基于多路复用对listening socket监听

“vert.x-acceptor-thread-0” #46 prio=5 os_prio=0 tid=0x00007f91401a5000
nid=0x3a runnable [0x00007f913bdfe000] java.lang.Thread.State:
RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
- locked <0x0000000688421ec8> (a io.netty.channel.nio.SelectedSelectionKeySet)
- locked <0x0000000688421eb8> (a java.util.Collections$UnmodifiableSet)
- locked <0x0000000688421ee0> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:101)
at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:68)
at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:810)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:457)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)

eventloop thread,select各个连接socket,当监听到请求数据时,执行相应的request handler;

“vert.x-eventloop-thread-0” #47 prio=5 os_prio=0
tid=0x00007f9140146800 nid=0x3b runnable [0x00007f913bcfd000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
- locked <0x000000068839bac8> (a io.netty.channel.nio.SelectedSelectionKeySet)
- locked <0x000000068839bab8> (a java.util.Collections$UnmodifiableSet)
- locked <0x000000068839bae0> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
at io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62)
at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:814)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:457)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)

tomcat nio线程模型:

tomcat和vert.x服务器nio模型对比_第2张图片

  • 一个acceptor线程:用来监听客户端连接请求;使用accept方法对监听socket进行阻塞式访问;
  • 一个Poller线程:acceptor线程监听到连接请求后,创建一个socket,将其注册到Poller的selector上;poller监听到socket有请求数据时,丢给执行线程;
  • 多个执行线程,执行相应的连接socket的reqquest handler;

以下是tomcat线程和堆栈:
acceptor线程:用来监听客户端连接请求;使用accept方法对监听socket进行阻塞式访问;

“http-nio-8080-Acceptor” #57 daemon prio=5 os_prio=0
tid=0x00007f20b5aad000 nid=0x44 runnable [0x00007f2018deb000]
java.lang.Thread.State: RUNNABLE at
sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method) at
sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:424)
at
sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:252)

  • locked <0x0000000706702a30> (a java.lang.Object) at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:551)
    at
    org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:80)
    at org.apache.tomcat.util.net.Acceptor.run(Acceptor.java:106) at
    java.lang.Thread.run(Thread.java:748)

Locked ownable synchronizers:

  • None

Poller线程:acceptor线程监听到连接请求后,创建一个socket,注册到Poller的selector上;poller监听到socket有请求数据时,丢给执行线程;

“http-nio-8080-ClientPoller” #56 daemon prio=5 os_prio=0
tid=0x00007f20b5db5000 nid=0x43 runnable [0x00007f2018eec000]
java.lang.Thread.State: RUNNABLE at
sun.nio.ch.EPollArrayWrapper.epollWait(Native Method) at
sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269) at
sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93) at
sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)

  • locked <0x0000000706702c68> (a sun.nio.ch.Util$3)
  • locked <0x0000000706702c78> (a java.util.Collections$UnmodifiableSet)
  • locked <0x0000000706702c20> (a sun.nio.ch.EPollSelectorImpl) at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97) at
    org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:793)
    at java.lang.Thread.run(Thread.java:748)

Locked ownable synchronizers:

  • None

执行线程,执行相应的连接socket的reqquest handler;

“http-nio-8080-exec-10” #55 daemon prio=5 os_prio=0
tid=0x00007f20b5def800 nid=0x42 waiting on condition
[0x00007f2018fed000] java.lang.Thread.State: TIMED_WAITING
(parking) at sun.misc.Unsafe.park(Native Method)

  • parking to wait for <0x0000000706703110> (a java.util.concurrent.locks.AbstractQueuedSynchronizer C o n d i t i o n O b j e c t ) a t j a v a . u t i l . c o n c u r r e n t . l o c k s . L o c k S u p p o r t . p a r k N a n o s ( L o c k S u p p o r t . j a v a : 215 ) a t j a v a . u t i l . c o n c u r r e n t . l o c k s . A b s t r a c t Q u e u e d S y n c h r o n i z e r ConditionObject) at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215) at java.util.concurrent.locks.AbstractQueuedSynchronizer ConditionObject)atjava.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)atjava.util.concurrent.locks.AbstractQueuedSynchronizerConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
    at
    java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
    at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:90)
    at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:33)
    at
    java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)
    at
    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
    at
    java.util.concurrent.ThreadPoolExecutor W o r k e r . r u n ( T h r e a d P o o l E x e c u t o r . j a v a : 624 ) a t o r g . a p a c h e . t o m c a t . u t i l . t h r e a d s . T a s k T h r e a d Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread Worker.run(ThreadPoolExecutor.java:624)atorg.apache.tomcat.util.threads.TaskThreadWrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

总结

vert.x/tomcat 相同点:

  • 两者都是有一个acceptor线程,专门监听listening socket;

  • 其他线程通过select来监听连接socket,并执行request handler;

    不同点:

  • tomcat acceptor线程使用accept,而vert.x使用select监听listening socket;

  • tomcat poller线程和执行线程分开;vert.x poller和select在同一个线程中;

你可能感兴趣的:(tomcat,服务器,java,云计算,架构)