dubbo服务调用源码解读

流程图

在这里插入图片描述

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看看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值