dubbo源码之一次RPC请求的生死之旅(基于Dubbo 2.7.8) - 指南
1. 全景图:从宏观到微观
在钻入代码之前,我们需要先在脑海中建立一张全景图。一次同步的 RPC 调用,大致可以分为三个阶段:
消费端(Consumer): 动态代理 -> 负载均衡 -> 封装请求 -> 编码发送 -> 同步阻塞等待。
服务端(Provider): 解码接收 -> 线程池派发 -> 过滤器链 -> 反射调用 -> 封装响应 -> 编码发送。
消费端(Consumer): 接收响应 -> 唤醒等待线程 -> 提取结果。
本文将略过配置加载和服务发现细节,通过核心链路代码将上述过程串联起来。
2. 第一阶段:消费端发起请求(Consumer)
当我们代码中执行 demoService.sayHello("world") 时,实际上是在调用 Dubbo 生成的代理对象。
2.1 动态代理入口
Dubbo 默认使用 Javassist 生成代理。所有的方法调用都会被转发到 InvokerInvocationHandler。
源码位置: org.apache.dubbo.rpc.proxy.InvokerInvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ... 省略 Object 类方法的处理
// 将参数封装为 RpcInvocation
RpcInvocation invocation = new RpcInvocation(method, serviceModel, args);
invocation.setTargetServiceUniqueName(invoker.getUrl().getServiceKey());
// invoker 是一层层包装的,这里开始进入链式调用
return invoker.invoke(invocation).recreate();
}
2.2 集群容错与负载均衡 (Cluster)
这里的 invoker 对象通常是 MockClusterInvoker 包装下的 FailoverClusterInvoker(默认故障转移策略)。
源码位置: org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker
public Result doInvoke(Invocation invocation, final List> invokers, LoadBalance loadbalance) {
// 1. 获取重试次数,默认 retries="2" (共调3次)
int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
for (int i = 0; i < len; i++) {
// 2. 负载均衡选择一个 Invoker (例如 DubboInvoker)
Invoker invoker = select(loadbalance, invocation, copyInvokers, invoked);
try {
// 3. 执行调用
Result result = invoker.invoke(invocation);
return result;
} catch (RpcException e) {
// 发生异常,循环继续,即“重试”
}
}
}
2.3 过滤器链 (Filter Chain)
在选定具体的 DubboInvoker 之前,请求会经过一系列 Filter(如 ConsumerContextFilter、MonitorFilter)。这是通过 ProtocolFilterWrapper 构建的责任链模式。
2.4 协议层发送 (Protocol)
请求最终到达 DubboInvoker,这里是 Dubbo 协议的核心。
源码位置: org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker
protected Result doInvoke(final Invocation invocation) throws Throwable {
// 获取 ExchangeClient (封装了 Netty Client)
ExchangeClient currentClient = clients[index.getAndIncrement() % clients.length];
// 区分单向调用、异步调用、同步调用
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
if (isOneway) {
// 单向调用,只发不回
currentClient.send(inv, getUrl().getMethodParameter(methodName, SENT_KEY, false));
return AsyncRpcResult.newDefaultAsyncResult(invocation);
} else if (isAsync) {
// 异步调用
ResponseFuture future = currentClient.request(inv, timeout);
// ... 返回 Future
} else {
// 【核心重点】同步调用 (默认)
RpcContext.getContext().setFuture(null);
// 发送请求,获得 DefaultFuture
return (Result) currentClient.request(inv, timeout).get();
}
}
2.5 交换层与同步等待 (Exchange)
currentClient.request 会调用 HeaderExchangeChannel.request。这是实现“同步转异步”的关键。
源码位置: org.apache.dubbo.remoting.exchange.support.DefaultFuture
// 发送请求
public ResponseFuture request(Object request, int timeout) throws RemotingException {
// 1. 创建请求对象,自动生成全局唯一 Request ID
Request req = new Request();
req.setData(request);
// 2. 创建 DefaultFuture,映射关系:Request ID -> Future
DefaultFuture future = newDefaultFuture(channel, req, timeout);
// 3. 通过 Netty 发送数据
channel.send(req);
return future;
}
紧接着,DubboInvoker 调用了 future.get(),线程在此阻塞。
// DefaultFuture.java
public Object get(int timeout) throws RemotingException {
// 使用 Condition.await 进行阻塞,等待服务端响应唤醒
if (!done) {
long start = System.currentTimeMillis();
lock.lock();
try {
while (!done) {
done.await(timeout, TimeUnit.MILLISECONDS);
// 超时检查逻辑...
}
} finally {
lock.unlock();
}
}
return returnFromResponse();
}
3. 第二阶段:服务端处理请求(Provider)
网络报文经过 TCP 传输到达服务端,Netty 接收到字节流。
3.1 线程派发 (Thread Model)
Netty 的 IO 线程(Worker Group)负责解码,解码后的消息会经过 AllChannelHandler(默认策略),将请求派发到 Dubbo 的业务线程池中去执行,避免阻塞 IO 线程。
源码位置: org.apache.dubbo.remoting.transport.dispatcher.all.AllChannelHandler
public void received(Channel channel, Object message) throws RemotingException {
// 获取业务线程池
ExecutorService executor = getExecutorService();
try {
// 将请求包装成 ChannelEventRunnable 丢给线程池执行
executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
} catch (Throwable t) {
// 线程池满的拒绝策略(报错)
}
}
3.2 交换层处理 (Exchange)
业务线程拿到请求后,层层传递,到达 HeaderExchangeHandler.received。它区分这是请求(Request)还是响应(Response)。
源码位置: org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler
public void received(Channel channel, Object message) {
if (message instanceof Request) {
handleRequest(channel, (Request) message);
} else if (message instanceof Response) {
handleResponse(channel, (Response) message);
}
}
void handleRequest(Channel channel, Request req) {
Response res = new Response(req.getId());
// 继续调用后续 Handler (DubboProtocol)
Object result = handler.reply(channel, req.getData());
res.setResult(result);
// 发送响应回客户端
channel.send(res);
}
3.3 协议层与反射调用 (Protocol)
handler.reply 最终会调用到 DubboProtocol.requestHandler。在这里,根据 ServiceKey 找到服务端暴露的 Exporter。
源码位置: org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
// 匿名内部类 requestHandler
public CompletableFuture
浙公网安备 33010602011771号