分析
dubbo与netty之间通过一个BlockQueue传输数据
一个是主线程([main,5,main]),通过调用threadlessExecutor.waitAndDrain()从一个LinkedBlockingQueue中获取数据。
另一个是netty的线程([NettyClientWorker,优先级不知道,main]),将数据存入LinkedBlockingQueue中。
主线程尝试获取数据
堆栈 主线程尝试从队列中获取数据
waitAndDrain:89, ThreadlessExecutor (org.apache.dubbo.common.threadpool)
get:179, AsyncRpcResult (org.apache.dubbo.rpc)
invoke:61, AsyncToSyncInvoker (org.apache.dubbo.rpc.protocol)//从这里发送和接收开始不同
invoke:78, ListenerInvokerWrapper (org.apache.dubbo.rpc.listener)
invoke:89, MonitorFilter (org.apache.dubbo.monitor.support)
invoke:81, ProtocolFilterWrapper$1 (org.apache.dubbo.rpc.protocol)
invoke:51, FutureFilter (org.apache.dubbo.rpc.protocol.dubbo.filter)
invoke:81, ProtocolFilterWrapper$1 (org.apache.dubbo.rpc.protocol)
invoke:69, ConsumerContextFilter (org.apache.dubbo.rpc.filter)
invoke:81, ProtocolFilterWrapper$1 (org.apache.dubbo.rpc.protocol)
invoke:56, InvokerWrapper (org.apache.dubbo.rpc.protocol)
doInvoke:82, FailoverClusterInvoker (org.apache.dubbo.rpc.cluster.support)
invoke:259, AbstractClusterInvoker (org.apache.dubbo.rpc.cluster.support)
intercept:47, ClusterInterceptor (org.apache.dubbo.rpc.cluster.interceptor)
invoke:92, AbstractCluster$InterceptorInvokerNode (org.apache.dubbo.rpc.cluster.support.wrapper)
invoke:82, MockClusterInvoker (org.apache.dubbo.rpc.cluster.support.wrapper)
invoke:74, InvokerInvocationHandler (org.apache.dubbo.rpc.proxy)
sayHello:-1, proxy0 (org.apache.dubbo.common.bytecode)
main:33, Consumer (org.apache.dubbo.samples.mock)
关键代码 主线程尝试从队列中获取数据
/**
* Waits until there is a task, executes the task and all queued tasks (if there're any). The task is either a normal
* response or a timeout response.
*/
public void waitAndDrain() throws InterruptedException {
/**
* Usually, {@link #waitAndDrain()} will only get called once. It blocks for the response for the first time,
* once the response (the task) reached and being executed waitAndDrain will return, the whole request process
* then finishes. Subsequent calls on {@link #waitAndDrain()} (if there're any) should return immediately.
*
* There's no need to worry that {@link #finished} is not thread-safe. Checking and updating of
* 'finished' only appear in waitAndDrain, since waitAndDrain is binding to one RPC call (one thread), the call
* of it is totally sequential.
*/
if (finished) {
return;
}
Runnable runnable = queue.take();//89行,如果当前没有数据,此处会阻塞
synchronized (lock) {
waiting = false;
runnable.run();
}
runnable = queue.poll();
while (runnable != null) {
try {
runnable.run();
} catch (Throwable t) {
logger.info(t);
}
runnable = queue.poll();
}
// mark the status of ThreadlessExecutor as finished.
finished = true;
}
netty线程将获取到的数据存入BlockQueue中,并唤醒主线程
堆栈 netty线程因插入新的数据,而唤醒阻塞的BlockQueue(下文会对BlockQueue进行简单介绍)
unpark:140, LockSupport (java.util.concurrent.locks)
tryMatch:265, SynchronousQueue$TransferStack$SNode (java.util.concurrent)
transfer:383, SynchronousQueue$TransferStack (java.util.concurrent)
offer:913, SynchronousQueue (java.util.concurrent)
execute:1371, ThreadPoolExecutor (java.util.concurrent)
caught:76, AllChannelHandler (org.apache.dubbo.remoting.transport.dispatcher.all)
caught:63, AbstractChannelHandlerDelegate (org.apache.dubbo.remoting.transport)
caught:63, AbstractChannelHandlerDelegate (org.apache.dubbo.remoting.transport)
caught:152, AbstractPeer (org.apache.dubbo.remoting.transport)
exceptionCaught:138, NettyClientHandler (org.apache.dubbo.remoting.transport.netty4)
invokeExceptionCaught:285, AbstractChannelHandlerContext (io.netty.channel)
notifyHandlerException:850, AbstractChannelHandlerContext (io.netty.channel)
invokeUserEventTriggered:331, AbstractChannelHandlerContext (io.netty.channel)
invokeUserEventTriggered:315, AbstractChannelHandlerContext (io.netty.channel)
fireUserEventTriggered:307, AbstractChannelHandlerContext (io.netty.channel)
channelIdle:371, IdleStateHandler (io.netty.handler.timeout)
run:494, IdleStateHandler$ReaderIdleTimeoutTask (io.netty.handler.timeout)
run:466, IdleStateHandler$AbstractIdleTask (io.netty.handler.timeout)
call:38, PromiseTask$RunnableAdapter (io.netty.util.concurrent)
run:125, ScheduledFutureTask (io.netty.util.concurrent)
safeExecute$$$capture:163, AbstractEventExecutor (io.netty.util.concurrent)
safeExecute:-1, AbstractEventExecutor (io.netty.util.concurrent)
runAllTasks:404, SingleThreadEventExecutor (io.netty.util.concurrent)
run:465, NioEventLoop (io.netty.channel.nio)
run:884, SingleThreadEventExecutor$5 (io.netty.util.concurrent)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:748, Thread (java.lang)
关键代码 netty线程因插入新的数据,而唤醒阻塞的BlockQueue
/**
* Makes available the permit for the given thread, if it
* was not already available. If the thread was blocked on
* {@code park} then it will unblock. Otherwise, its next call
* to {@code park} is guaranteed not to block. This operation
* is not guaranteed to have any effect at all if the given
* thread has not been started.
*
* @param thread the thread to unpark, or {@code null}, in which case
* this operation has no effect
*/
public static void unpark(Thread thread) {
if (thread != null)//140行
UNSAFE.unpark(thread);
}
主线程被唤醒
堆栈 主线程从BlockQueue中成功获取到数据
waitAndDrain:91, ThreadlessExecutor (org.apache.dubbo.common.threadpool)
get:179, AsyncRpcResult (org.apache.dubbo.rpc)
invoke:61, AsyncToSyncInvoker (org.apache.dubbo.rpc.protocol)
invoke:78, ListenerInvokerWrapper (org.apache.dubbo.rpc.listener)
invoke:89, MonitorFilter (org.apache.dubbo.monitor.support)
invoke:81, ProtocolFilterWrapper$1 (org.apache.dubbo.rpc.protocol)
invoke:51, FutureFilter (org.apache.dubbo.rpc.protocol.dubbo.filter)
invoke:81, ProtocolFilterWrapper$1 (org.apache.dubbo.rpc.protocol)
invoke:69, ConsumerContextFilter (org.apache.dubbo.rpc.filter)
invoke:81, ProtocolFilterWrapper$1 (org.apache.dubbo.rpc.protocol)
invoke:56, InvokerWrapper (org.apache.dubbo.rpc.protocol)
doInvoke:82, FailoverClusterInvoker (org.apache.dubbo.rpc.cluster.support)
invoke:259, AbstractClusterInvoker (org.apache.dubbo.rpc.cluster.support)
intercept:47, ClusterInterceptor (org.apache.dubbo.rpc.cluster.interceptor)
invoke:92, AbstractCluster$InterceptorInvokerNode (org.apache.dubbo.rpc.cluster.support.wrapper)
invoke:82, MockClusterInvoker (org.apache.dubbo.rpc.cluster.support.wrapper)
invoke:74, InvokerInvocationHandler (org.apache.dubbo.rpc.proxy)
sayHello:-1, proxy0 (org.apache.dubbo.common.bytecode)
main:33, Consumer (org.apache.dubbo.samples.mock)
关键代码
/**
* Waits until there is a task, executes the task and all queued tasks (if there're any). The task is either a normal
* response or a timeout response.
*/
public void waitAndDrain() throws InterruptedException {
/**
* Usually, {@link #waitAndDrain()} will only get called once. It blocks for the response for the first time,
* once the response (the task) reached and being executed waitAndDrain will return, the whole request process
* then finishes. Subsequent calls on {@link #waitAndDrain()} (if there're any) should return immediately.
*
* There's no need to worry that {@link #finished} is not thread-safe. Checking and updating of
* 'finished' only appear in waitAndDrain, since waitAndDrain is binding to one RPC call (one thread), the call
* of it is totally sequential.
*/
if (finished) {
return;
}
Runnable runnable = queue.take();
synchronized (lock) {//第91行,唤醒之后主线程接着运行,此时"runnable"这个对象中已经存有接收的数据了
waiting = false;
runnable.run();
}
runnable = queue.poll();
while (runnable != null) {
try {
runnable.run();
} catch (Throwable t) {
logger.info(t);
}
runnable = queue.poll();
}
// mark the status of ThreadlessExecutor as finished.
finished = true;
}
调试过程中的截图
有关多线程 BlockQueue的调试
在其他线程中添加对象并解锁