流程图
1.proxy代理类:helloService.sayHello
2.InvokerInvocationHandler
public Object invoke(Object proxy, Method method, Object[] args)
3.MockClusterInvoker
public Result invoke(Invocation invocation)
4.AbstractClusterInvoker
负载策略实现类:
faliBack failFast failOver failSafe forking broadcast(广播?)
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance)
关键步骤 选择一个提供者调用 使用loadbalance选择invoker
Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
Result result = invoker.invoke(invocation);
dubbo大量使用了invoker概念 这里返回的invoker虽然是一个接口,可实际是完全不同的逻辑,这里的invoker是对接口提供者的封装,请勿与以上一层套一层的invoker混淆
CircuitBreakerInvoker 路由invoker
//异步标志设置
boolean isAsync = getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false);
if (isAsync) {
invocation.setAttachment(Constants.ASYNC_KEY, String.valueOf(isAsync));
}
这个不是调用流程,服务引入是就根据URL设置好调用链了
责任链模式 这样不用每次调用都来初始化调用链,此处不展开讨论,详见服务引入篇
com.alibaba.dubbo.rpc.Protocol#refer //接口设计的应用入口
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// 将URL列表转成Invoker列表
filter责任链
invoker持有filter filter.invoke(Invoker<?> invoker, Invocation invocation)方法参数里传入下一个invoker (责任链写得有点炫技了吧)
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (filters.size() > 0) {
for (int i = filters.size() - 1; i >= 0; i --) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
public Class<T> getInterface() {
return invoker.getInterface();
}
public URL getUrl() {
return invoker.getUrl();
}
public boolean isAvailable() {
return invoker.isAvailable();
}
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
return last;
}
com.alibaba.dubbo.rpc.cluster.support.CircuitBreakerInvoker.state.get().invoke
第一个filter开始invoker (一路带下来的上下文,为了抽象和命名方便,所以真是无处不在的invoke)
filter接口设计
public interface Filter {
Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
}
DubboInvoker
最后一个invoker就不是以上生成的filter匿名类,而是ListenerInvokerWrapper
ListenerInvokerWrapper此有rpc调用关键的invoker DubboInvoker
这里有异步/同步/不关心返回 三种策略的处理逻辑
主要是这一句 因为底层用的netty传输 上层得到的就是future 对future的区别处理就产生了不同的传输模式逻辑
ResponseFuture future = currentClient.request(inv, timeout);
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null);
return new RpcResult();
} else if (isAsync) {
//不能将这个传递到provider了
invocation.getAttachments().remove(Constants.ASYNC_KEY);
ResponseFuture future = currentClient.request(inv, timeout);
FutureAdapter<Object> adapter = new FutureAdapter<Object>(future);
RpcContext.getContext().setFuture(adapter);
return new AsyncRpcResult(future);
} else {
RpcContext.getContext().setFuture(null);
return (Result) currentClient.request(inv, timeout).get();
}
再深入ExchangeClient
dubbo对exchange这一层有个定义:
exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
首先进入com.alibaba.dubbo.rpc.protocol.dubbo.ReferenceCountExchangeClient 只是做引用计数
看一下字段定义就明白它干的是什么事了
final class ReferenceCountExchangeClient implements ExchangeClient {
private ExchangeClient client;
private final URL url;
private final AtomicInteger refenceCount = new AtomicInteger(0);
...
}
从ReferenceCountExchangeClient#client进入到HeaderExchangeChannel
开始有了channel通道的概念
final class HeaderExchangeChannel implements ExchangeChannel {
private static final Logger logger = LoggerFactory.getLogger(HeaderExchangeChannel.class);
private static final String CHANNEL_KEY = HeaderExchangeChannel.class.getName() + ".CHANNEL";
private final Channel channel;
private volatile boolean closed = false;
...
}
dubbo通信层有netty和mina,本文只讨论netty.这里的channel其实和netty的channel是一一对应的 通道唯一性的确定来源于socket通信,一个socket里面包含通信双方的ip和端口,先来追一下channel链
dubbo: (NettyClient)HeaderExchangeChannel.channel
Netty: (NioClientSocketChannel)NettyClient.channel
NioClientSocketChannel父类AbstractNioChannel里面是维护两个socket信息的,这也印证的上文
volatile InetSocketAddress localAddress;
volatile InetSocketAddress remoteAddress;
主要代码:
1.初始化DefaultFuture
2.调用netty channel的send方法
DefaultFuture future = new DefaultFuture(channel, req, timeout);
try {
channel.send(req);
} catch (RemotingException e) {
future.cancel();
throw e;
}
经历了漫长的初始化操作,终于要进入正题,下面我们看看dubbo怎么和netty结合的,看一下Future的设计,先贴一下源码,从字段定义大概能看出个端倪
1.DefaultFuture类维护了static的Map<Long, DefaultFuture> FUTURES,所有DefaultFuture对象初始化时都要现在里面注册
2.request.getId()一次调用的唯一标识,这个id会发给provider,provider返回再带回来,这也解释了dubbo在 netty channel的全双工通信地基上
能做到相同接口同时调用互不干扰。
3.DefaultFuture的静态方法会启动超时检测线程,如果第1步注册的Future已经超时,解开future.get(),返回超时结果.(Tip:此处开的线程是守护线程,父线程死掉也不会关闭)
4.private final Lock lock = new ReentrantLock();使用重入锁保护可能会产生线程冲突的逻辑
public class DefaultFuture implements ResponseFuture {
private static final Logger logger = LoggerFactory.getLogger(DefaultFuture.class);
private static final Map<Long, Channel> CHANNELS = new ConcurrentHashMap<Long, Channel>();
private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<Long, DefaultFuture>();
// invoke id.
private final long id;
private final Channel channel;
private final Request request;
private final int timeout;
private final Lock lock = new ReentrantLock();
private final Condition done = lock.newCondition();
private final long start = System.currentTimeMillis();
private volatile long sent;
private volatile Response response;
private volatile ResponseCallback callback;
public DefaultFuture(Channel channel, Request request, int timeout) {
this.channel = channel;
this.request = request;
this.id = request.getId();
this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
// put into waiting map.
FUTURES.put(id, this);
CHANNELS.put(id, channel);
}
}
第3步源码
private static class RemotingInvocationTimeoutScan implements Runnable {
public void run() {
while (true) {
try {
for (DefaultFuture future : FUTURES.values()) {
if (future == null || future.isDone()) {
continue;
}
if (System.currentTimeMillis() - future.getStartTimestamp() > future.getTimeout()) {
// create exception response.
Response timeoutResponse = new Response(future.getId());
// set timeout status.
timeoutResponse.setStatus(future.isSent() ? Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT);
timeoutResponse.setErrorMessage(future.getTimeoutMessage(true));
// handle response.
DefaultFuture.received(future.getChannel(), timeoutResponse);
}
}
Thread.sleep(30);
} catch (Throwable e) {
logger.error("Exception when scan the timeout invocation of remoting.", e);
}
}
}
}
static {
Thread th = new Thread(new RemotingInvocationTimeoutScan(), "DubboResponseTimeoutScanTimer");
th.setDaemon(true);
th.start();
}
向外暴露recieved方法,由netty channel的 handler回调机制调用
public static void received(Channel channel, Response response) {
try {
DefaultFuture future = FUTURES.remove(response.getId());
if (future != null) {
future.doReceived(response);
} else {
logger.warn("The timeout response finally returned at "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))
+ ", response " + response
+ (channel == null ? "" : ", channel: " + channel.getLocalAddress()
+ " -> " + channel.getRemoteAddress()));
}
} finally {
CHANNELS.remove(response.getId());
}
}
channel回调链
1.NettyHandler#messageReceived(这个方法是netty和dubbo的交互桥梁,NettyHandler父类是netty的SimpleChannelHandler,NettyHandler注册在netty channel上,当netty channel监测到 读/写/连接事件后会调用Handler对应的方法,详细请自行看netty设计)
这里贴一下NettyHandler注册的netty channel的源码(channel这一步用的是NioSocketChannel,可见底层用的还是jdk nio,linux上使用EpollServerSocketChannel效率更吧)
@Override
protected void doOpen() throws Throwable {
final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
bootstrap = new Bootstrap()
.group(NettyHandler.workerGroup)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getTimeout())
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
ch.pipeline().addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
.addLast("handler", nettyHandler);
}
});
}
2.AbstractPeer#received
3.MultiMessageHandler#received
4.HeartbeatHandler#received
5.AllChannelHandler#received
6.ChannelEventRunnable#run(这里开始使用dubbo线程池 consumer channel默认使用share共享线程池),详见服务引入
protected static ChannelHandler wrapChannelHandler(URL url, ChannelHandler handler) {
url = ExecutorUtil.setThreadName(url, CLIENT_THREAD_POOL_NAME);
url = url.addParameterIfAbsent(Constants.THREADPOOL_KEY, Constants.DEFAULT_CLIENT_THREADPOOL);
return ChannelHandlers.wrap(handler, url);
}
7.DecodeHandler.received
8.HeaderExchangeHandler#received
9.DefaultFuture#received
回调说完整个调用链就全通了, 有兴趣可再深入netty看看