五Dubbo服务引用源码分析--4网络处理-4.1consumer端发送请求
五Dubbo服务引用源码分析--4网络处理-4.1consumer端发送请求
5.3 服务请求网络处理(connect、received)
本章节中,主要分析的consumer<----->provider两端底层通信过程中,关于provider端对于connect连接消息处理、consumer端发送请求、provider处理请求、consumer端接收响应的过程。涉及Dubbo的Exchange、Transport、Serialize三个逻辑层次上的过程。
Exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer。
Transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec。
Serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool。
消息的发送与接收,以及对于消息处理的channelHandler,都是通过底层Netty通信框架处理。
5.3.1 provider端处理connect消息(补充)
provider端在处理connect连接请求时,没有什么处理,不再具体跟进代码分析。
5.3.2 consumer端发送请求
此时,consumer端调用服务,会执行到DubboInvoker.invoke处,来到了Protocol-->Exchange层
TAG1 AbstractInvoker.invoke
AbstractInvoker
@Override
public Result invoke(Invocation inv) throws RpcException {
// if invoker is destroyed due to address refresh from registry, let's allow the current invoke to proceed
if (destroyed.get()) {
logger.warn("Invoker for service " + this + " on consumer " + NetUtils.getLocalHost() + " is destroyed, "
+ ", dubbo version is " + Version.getVersion() + ", this invoker should not be used any longer");
}
//methodName=getPermission,arg=1
RpcInvocation invocation = (RpcInvocation) inv;
invocation.setInvoker(this);//将当前DubboInvoker设置到invocation调用类中
//interface -> com.alibaba.dubbo.demo.DemoService attachment
if (attachment != null && attachment.size() > 0) {
invocation.addAttachmentsIfAbsent(attachment);
}
Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
if (contextAttachments != null && contextAttachments.size() != 0) {
/**
* invocation.addAttachmentsIfAbsent(context){@link RpcInvocation#addAttachmentsIfAbsent(Map)}should not be used here,
* because the {@link RpcContext#setAttachment(String, String)} is passed in the Filter when the call is triggered
* by the built-in retry mechanism of the Dubbo. The attachment to update RpcContext will no longer work, which is
* a mistake in most cases (for example, through Filter to RpcContext output traceId and spanId and other information).
*/
invocation.addAttachments(contextAttachments);
}
if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) {
invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
}
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
try {
//TAG2 DubboInvoker.doInvoke
//由子类DubboInvoker.doInvoke实现调用逻辑
return doInvoke(invocation);
} catch (InvocationTargetException e) { // biz exception
Throwable te = e.getTargetException();
if (te == null) {
return new RpcResult(e);
} else {
if (te instanceof RpcException) {
((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
}
return new RpcResult(te);
}
} catch (RpcException e) {
if (e.isBiz()) {
return new RpcResult(e);
} else {
throw e;
}
} catch (Throwable e) {
return new RpcResult(e);
}
}
TAG2 DubboInvoker.doInvoke(调用是否异步及处理)
DubboInvoker
@Override
protected Result doInvoke(final Invocation invocation) throws Throwable {
/** ………………………………………………………………向RpcInvocation中添加属性信息……………………………………………………*/
RpcInvocation inv = (RpcInvocation) invocation;
final String methodName = RpcUtils.getMethodName(invocation);
//getUrl()=dubbo://192.168.0.101:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demotest-consumer&bean.name=com.alibaba.dubbo.demo.DemoService&check=false&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=42298&qos.enable=false®ister.ip=192.168.0.101&remote.timestamp=1672565419921&side=consumer×tamp=1672565435211-----服务提供者providerUrl
//设置RpcInvocation的path=RpcInvocation
inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
inv.setAttachment(Constants.VERSION_KEY, version);
/** ……………………………………获取客户端………………………………………………*/
ExchangeClient currentClient;
if (clients.length == 1) {
currentClient = clients[0];
} else {
currentClient = clients[index.getAndIncrement() % clients.length];//如果多个client,递增选择下一个
}
try {
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);//false,默认为非异步--同步
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);//false,默认非单向--双向(双向有返回值)
//默认1000ms延迟
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
/**…………………………………………………………………………………………同步、异步处理……………………………………………………………………………………………………………… */
//TAG2.1 请求服务同步、异步、是否需要返回值的处理
//不需要返回值的同步请求
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
//发起同步请求
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null);//设置RpcContext.future为null;
return new RpcResult();
} else if (isAsync) {
//发起异步异步请求
ResponseFuture future = currentClient.request(inv, timeout);
//将future包装为FutureAdapter,并传入RpcContext.future
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();
} else {
//需要返回值的同步请求
RpcContext.getContext().setFuture(null);///设置RpcContext.future为null
/** …………………………………………………………………………………………客户端exchangeClient.request远程调用……………………………………………………………………………………*/
//TAG2.2 ReferenceCountExchangeClient.request(inv,timeout)
//发送异步请求得到ResponseFuture,然后执行ResponseFuture.get(),阻塞consumer线程,获取执行结果
return (Result) currentClient.request(inv, timeout).get();
}
} catch (TimeoutException e) {
throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
} catch (RemotingException e) {
throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
这里,获取客户端ExchangeClient
TAG2.1 DubboInvoker中,请求服务同步、异步、是否需要返回值的处理
在DubboInvoker.doInvoker方法中,通过ExchangeClient发送请求前,需要判断当前请求的url、invocation中的参数,获取配置的isAsync(异步)、isOneway(单向---即不需要返回值),根据这两个参数,进行对应的请求发送逻辑:
1 如果isOneway--即不需要返回值(同步)
currentClient.send(inv, isSent);//同步发送消息
RpcContext.getContext().setFuture(null);//设置RpcContext.future为null;
return new RpcResult();//返回null的RpcResult
2 如果isAsync--即异步请求
ResponseFuture future = currentClient.request(inv, timeout);//异步请求,返回异步结果
//将future包装为FutureAdapter,并传入RpcContext.future
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();//返回null的RpcResult
3 其他情况--即需要返回值的同步请求
RpcContext.getContext().setFuture(null);///设置RpcContext.future为null
/** …………………………………………………………………………………………客户端exchangeClient.request远程调用……………………………………………………………………………………*/
//TAG2.2 ReferenceCountExchangeClient.request(inv,timeout)
//发送异步请求得到ResponseFuture,然后执行ResponseFuture.get(),阻塞consumer线程,获取执行结果
return (Result) currentClient.request(inv, timeout).get();
//注意,这里虽然调用currentClient.request这个异步请求获取到ResponseFuture,但是ResponseFuture.get()仍旧是阻塞式的等待获取provider端的结果
注意:
client.send是同步请求,不返回结果;client.request是发送异步请求,并返回ResponseFuture异步结果。
而同步请求的情况下,ResponseFuture.get()是阻塞等待provider端处理结果;而异步请求,直接将ResponseFuture包装成FutureAdapter,返回到dubbo的rpc上下文对象RpcContext.getContext().setFuture(new FutureAdapter