【dubbo源码分析】6. dubbo消费端-Consumer如何通过引用代理调用具体服务方法

11 篇文章 0 订阅
由文章 5.ProxyFactory使用 Invoker创建消费端调用代理类 DemoService引用代理类为:
因此,当我们调用 demoService.sayHello("world") 方法时,将会调用代理类proxy0.sayHello()
public class proxy0 implements ClassGenerator.DC, EchoService, DemoService {
  // methods包含proxy0实现的所有接口方法(去重)
  public static Method[] methods; //methods = {syaHello,$echo}
  private InvocationHandler handler;

  public String sayHello(String arg0) {
    Object[] args = new Object[1];
    args[0] = arg0;
    Object localObject = this.handler.invoke(this, methods[0], args);
    return (String)localObject;
  }
}
总体交互流程如下:





调用处理链为:
proxy0.sayHello() -> InvocationHandler.invoke(Object proxy, Method method, Object[] args) -> MockClusterInvoker.incoke(Invocation invocation) -> AbstractClusterInvoker.invoke(Invocation invocation) (AvalibleCluster内) -> AbstractClusterInvoker.doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) (AvalibleCluster内)-> Invoker.invoke() 
接下来进行源码分析:
    我们先看看MockClusterInvoker.invoke()方法:
主要流程: 1. 无mock设置,直接走正常流程
2.mock=force:*** 则直接走mock返回结果
3. 首先尝试调用invoke,如果出现异常则调用mock进行返回

 public Result invoke(Invocation invocation) throws RpcException {
        Result result = null;

        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
        if (value.length() == 0 || value.equalsIgnoreCase("false")) {
            //no mock
            result = this.invoker.invoke(invocation);   //无mock设置,直接走正常流程
        } else if (value.startsWith("force")) {
            if (logger.isWarnEnabled()) {
                logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
            }
            //force:direct mock
            result = doMockInvoke(invocation, null);   //mock=force:*** 则直接走mock返回结果
        } else {
            //fail-mock
            try {
                result = this.invoker.invoke(invocation);  //mock=fail:*** 首先尝试调用invoke,如果出现异常则调用mock进行返回
            } catch (RpcException e) {
                if (e.isBiz()) {
                    throw e;
                } else {
                    if (logger.isWarnEnabled()) {
                        logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
                    }
                    result = doMockInvoke(invocation, e);
                }
            }
        }
        return result;
    }
接着执行 AvalibleCluster内 Invoker:(AvalibleCluster内的Invoker为一个 AbstractClusterInvoker 匿名类)
public class AvailableCluster implements Cluster {
    public static final String NAME = "available";
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {

        return new AbstractClusterInvoker<T>(directory) {
            public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
                for (Invoker<T> invoker : invokers) {
                    if (invoker.isAvailable()) {
                        return invoker.invoke(invocation);  //doInvoke方法会选择任意一个可用的Invoker执行
                    }
                }
                throw new RpcException("No provider available in " + invokers);
            }
        };
    }
}
此时的Invoker为 RegistryProtocol在创建refer invoker 时构建的实例:

因此,在选择任意一个可用Invoker之后,类的调用关系为:
MockClusterInvoker .invoke(Invocation invocation) -> FailoverClusterInvoker.invoke() -> RegistryDirectory.InvokerDelegete.invoke()
-> ListenerInvokerWrapper.invoke()
-> ProtocolFilterWrapper.invoke() ConsumerContextFilter -> MonitorFilter -> FutureFilter
-> DubboInvoker.invoke() -> DubboInvoker.doInvoke()

注:
1.RegistryDirectory.InvokerDelegete 实例何时创建的可以查看 RegistryDirectory.refreshInvokers -> toInvokers() 时创建的
2.ListenerInvokerWrapper 为创建refer时 ProtocolListenerWrapper.refer进行创建的实例:
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
                Collections.unmodifiableList(
                        ExtensionLoader.getExtensionLoader(InvokerListener.class)
                                .getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));
    }
3.ProtocolFilterWrapper.invoke()为调用consumer端过滤器Filter链: 【调用链为: ConsumerContextFilter -> MonitorFilter -> FutureFilter】
创建Filter链为ProtocolFilterWrapper类如下代码片段:
  public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
    }

DubboInvoker.invoke发起远程调用底层通讯交互如下:

Netty网络传输与服务器交互流程图:


先看看 DubboInvoker.doInvoke()源码:

protected Result doInvoke(final Invocation invocation) throws Throwable {
        RpcInvocation inv = (RpcInvocation) invocation;
        final String methodName = RpcUtils.getMethodName(invocation);
        inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
        inv.setAttachment(Constants.VERSION_KEY, version);

/**1.首选会获取连接,如果是共享连接,直接获取,
 *如果服务启动的是多个连接,那么根据 AtomicPositiveInteger index = new AtomicPositiveInteger() 对连接数取余的方式来选择连接 */

        ExchangeClient currentClient;
        if (clients.length == 1) {
            currentClient = clients[0];
        } else {
            currentClient = clients[index.getAndIncrement() % clients.length];
        }
        try {
/**2.接着判断是否是异步调用,和单路调用(无返回,只管调用)*/
            boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
            boolean isOneway = RpcUtils.isOneway(getUrl(), invocation); 	//先判断服务是否设置 return=false,如果是  isOneWay=true;否则判断Method是否设置了 return(方法级的默认为true)
            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) { //异步调用,将Future设置到ThreadLocal中
                ResponseFuture future = currentClient.request(inv, timeout);
                RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
                return new RpcResult();
            } else { //同步调用,等待调用结果后才返回
                RpcContext.getContext().setFuture(null);
                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);
        }
    }
接着使用 currentClient.request 进行消息发送:经过 ReferenceCountExchangeClient【统计数据调用次数】类后传入 HeaderExchangeClient.request(),接着在HeaderExchangeChannel.request处进行处理最终发送:[构建request,设置版本号,单双路模式等等],源码如下:
HeaderExchangeChannel.java
public ResponseFuture request(Object request, int timeout) throws RemotingException {
        if (closed) {
            throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
        }
        // 创建Request,Request构造函数会设置本次Request的mId,作为本次调用的标识。
//设置版本号,数据,单双路模式
        Request req = new Request();
        req.setVersion("2.0.0");
        req.setTwoWay(true);
        req.setData(request);
        DefaultFuture future = new DefaultFuture(channel, req, timeout);  //创建DefaultFuture,并根据Request中的mId作为requestId将Future对象缓存起来
        try {
            channel.send(req);   //执行发送 转入NettyClient.send
        } catch (RemotingException e) {
            future.cancel();
            throw e;
        }
        return future;
    }

NettyClient.java send方法:
public void send(Object message, boolean sent) throws RemotingException {
        if (send_reconnect && !isConnected()) {
            connect();
        }
        Channel channel = getChannel();  //getChannel会根据NioSocketClientChannel来获取缓存中的NettyChannel,如果不存在,则新建 NettyChannel 返回
        if (channel == null || !channel.isConnected()) {
            throw new RemotingException(this, "message can not send, because channel is closed . url:" + getUrl());
        }
        channel.send(message, sent);
    }


    protected com.alibaba.dubbo.remoting.Channel getChannel() {
        Channel c = channel;
        if (c == null || !c.isConnected())
            return null;
        return NettyChannel.getOrAddChannel(c, getUrl(), this);
    }


static NettyChannel getOrAddChannel(org.jboss.netty.channel.Channel ch, URL url, ChannelHandler handler) {
        if (ch == null) {
            return null;
        }
        NettyChannel ret = channelMap.get(ch);
        if (ret == null) {
            NettyChannel nc = new NettyChannel(ch, url, handler);
            if (ch.isConnected()) {
                ret = channelMap.putIfAbsent(ch, nc);
            }
            if (ret == null) {
                ret = nc;
            }
        }
        return ret;
    }

这里 nettyClient首先会判断当前连接的Channel是否正常,如果不正常连接,则重新获取connect,接着获取NettyChannel 后使用channel发送数据:

NettyChannel.java
public void send(Object message, boolean sent) throws RemotingException {
        super.send(message, sent);

        boolean success = true;
        int timeout = 0;
        try {
            ChannelFuture future = channel.write(message);//往 NioSocketClientChannel 写入数据,返回的Future等待timeout时间
            if (sent) {
                timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
                success = future.await(timeout);
            }
            Throwable cause = future.getCause();
            if (cause != null) {
                throw cause;
            }
        } catch (Throwable e) {
            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress() + ", cause: " + e.getMessage(), e);
        }

        if (!success) {
            throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress()
                    + "in timeout(" + timeout + "ms) limit");
        }
    }
NioSocketClientChannel写入数据流后 会进入到Netty底层的通讯,调用类过程为:
NioSocketClientChannel.write() -> org . jboss . netty . channel .Channels.write(Channel channel, Object message) -> Channels.write(Channel channel, Object message, SocketAddress remoteAddress) 设置下行数据流 -> DefaultChannelPipeline.sendDownStream()
->:

Channels.java
public static ChannelFuture write(Channel channel, Object message, SocketAddress remoteAddress) {
        ChannelFuture future = future(channel); //构建Future
//设置Netty下行流
        channel.getPipeline().sendDownstream(
                new DownstreamMessageEvent(channel, future, message, remoteAddress));
        return future;
    }
org . jboss . netty . channel .DefaultChannelPipeline .java
void sendDownstream(DefaultChannelHandlerContext ctx, ChannelEvent e) {
        if (e instanceof UpstreamMessageEvent) {
            throw new IllegalArgumentException("cannot send an upstream event to downstream");
        }
        
        try {
//这里开始获取Handler链进行处理,获取到的Handler为创建NettyClient实例时创建的  NettyHandler
            ((ChannelDownstreamHandler) ctx.getHandler()).handleDownstream(ctx, e);
        } catch (Throwable t) {
            e.getFuture().setFailure(t);
            notifyHandlerException(e, t);
        }
    }
NettyHandler.handleDownstream()会判断事件类型进行相应处理,这里为调用 NettyHandler.writeRequested方法进行发送数据
  public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
//这里将与服务端正式进行数据交互
        super.writeRequested(ctx, e);
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
        try {
//接着调用Handler链将前面构造的请求对象数据 Request向上进行回写
            handler.sent(channel, e.getMessage());
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
        }
    }
在设置Netty下行流的时候会获取NettyHandler,这里首先会触发Netty对输入数据进行encode,类调用关系如下图:
具体使用的是什么编解码方式,可以查看创建NettyClient时的设置:
NettyClient.java
 public NettyClient(final URL url, final ChannelHandler handler) throws RemotingException {
        super(url, wrapChannelHandler(url, handler));
    }
父类 AbstractEndpoint.java
 public AbstractEndpoint(URL url, ChannelHandler handler) {
        super(url, handler);
        this.codec = getChannelCodec(url);
        this.timeout = url.getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
        this.connectTimeout = url.getPositiveParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT);
    }

    protected static Codec2 getChannelCodec(URL url) {
        String codecName = url.getParameter(Constants.CODEC_KEY, "telnet");
        if (ExtensionLoader.getExtensionLoader(Codec2.class).hasExtension(codecName)) {
            return ExtensionLoader.getExtensionLoader(Codec2.class).getExtension(codecName);
        } else {
            return new CodecAdapter(ExtensionLoader.getExtensionLoader(Codec.class)
                    .getExtension(codecName));
        }
    }
而在创建NettyClient时 DubboProtocol.initClient方法初始化连接时指定了codec方式: url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
由此可知,codec= com.alibaba.dubbo.rpc.protocol.dubbo.DubboCountCodec ( 详情可查看文章4 )

又因为NettyHandler创建时封装了上层传递进来的Handler链,如下图:


因此,输入信息编码完成后,将调用Handler链层层调用,最终调用到DubboProtocol的 ExchangeHandlerAdapter ;
发送完成后HeaderExchangeHandler会将发送的Request设置回去








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Apache Dubbo 是一款高性能、轻量级的开源 Java 服务框架。 功能特点: Apache Dubbo提供了六大核心能力:面向接口代理的高性能RPC调用,智能容错和负载均衡,服务自动注册和发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。 1、面向接口代理的高性能RPC调用 提供高性能的基于代理的远程调用能力,服务以接口为粒度,为开发者屏蔽远程调用底层细节。 2、智能负载均衡 内置多种负载均衡策略,智能感知下游节点健康状况,显著减少调用延迟,提高系统吞吐量。 3、服务自动注册与发现 支持多种注册中心服务服务实例上下线实时感知。 4、高度可扩展能力 遵循微内核+插件的设计原则,所有核心能力如Protocol、Transport、Serialization被设计为扩展点,平等对待内置实现和第三方实现。 5、运行期流量调度 内置条件、脚本等路由策略,通过配置不同的路由规则,轻松实现灰度发布,同机房优先等功能。 6、可视化的服务治理与运维 提供丰富服务治理、运维工具:随时查询服务元数据、服务健康状态及调用统计,实时下发路由策略、调整配置参数。 Apache Dubbo 更新日志: v2.6.10.1 注意 1. 脚本路由器 默认情况下不会激活 ScriptRouter。如果用户还想使用它,请在selfs spi文件中添加scriptRouterFactory。 看dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory 2. 基因调用 默认情况下不会激活本机 Java 反序列化。如果用户仍然想使用它,请设置dubbo.security.serialize.generic.native-java-enable为true环境。 3. 序列化块列表 中引入了嵌入式序列化块列表dubbo-common/src/main/resources/security/serialize.blockedlist。 如果用户想添加允许列表,请参考dubbo.security.serialize.allowedClassList。 4. 序列号检查 可以启用一个可选的检查器来检查消费者是否发送了允许的序列化 ID,消费者是否收到了与发送给提供者相同的序列化 ID。您可以设置serialization.security.check为true启用此功能。 更改列表 疏散不必要的例子 初始化、创建 资源成本 修复 TPS 限制器在动态配置下不起作用 解决懒惰模式下共享连接不共享的问题 在 FailoverClusterInvoker 中修复 methodName 和重试 修复 Dubbo qos 命令对离线提供程序不起作用 为netty4客户添加socks5代理支持 支持高版本Nacos 添加一些序列化检查 修复 MonitorService 缺少 side=consumer 参数问题 修复 netty3 积压 修复反序列化漏洞

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值