dubbo远程调用源码分析(一):客户端发送请求_isinjvm 怎么判断

文章详细描述了Dubbo框架中消费者端如何集成配置信息,构建映射表,通过createProxy方法生成动态代理,以及涉及的各类Invoker实现,如AvailableClusterInvoker、FailOverClusterInvoker等,展示了远程调用和服务交互的流程。
摘要由CSDN通过智能技术生成
        if (module == null) {

            module = consumer.getModule();

        }

        if (registries == null) {

            registries =consumer.getRegistries();

        }

        if (monitor == null) {

            monitor =consumer.getMonitor();

        }

    }

    if (module != null) {

        if (registries == null) {

            registries = module.getRegistries();

        }

        if (monitor == null) {

            monitor = module.getMonitor();

        }

    }

    if (application != null) {

        if (registries == null) {

            registries =application.getRegistries();

        }

        if (monitor == null) {

            monitor =application.getMonitor();

        }

    }

    checkApplication();

    checkStubAndMock(interfaceClass);

    Map<String, String> map = new HashMap<String, String>();

    Map<Object, Object> attributes =new HashMap<Object, Object>();

    map.put(Constants.SIDE_KEY,Constants.CONSUMER_SIDE);

    map.put(Constants.DUBBO_VERSION_KEY,Version.getVersion());

   map.put(Constants.TIMESTAMP_KEY,String.valueOf(System.currentTimeMillis()));

    if (ConfigUtils.getPid() > 0) {

        map.put(Constants.PID_KEY,String.valueOf(ConfigUtils.getPid()));

    }

    if (!isGeneric()) {

        String revision =Version.getVersion(interfaceClass, version);

        if (revision != null &&revision.length() > 0) {

            map.put("revision",revision);

        }



        String[] methods =Wrapper.getWrapper(interfaceClass).getMethodNames();

        if (methods.length == 0) {

            logger.warn("NO method found in service interface " + interfaceClass.getName());

            map.put("methods",Constants.ANY_VALUE);

        } else {

            map.put("methods",StringUtils.join(new HashSet<String>(Arrays.asList(methods)),","));

        }

    }

    map.put(Constants.INTERFACE_KEY,interfaceName);

    appendParameters(map, application);

    appendParameters(map, module);

    appendParameters(map, consumer,Constants.DEFAULT_KEY);

    appendParameters(map, this);

    String prifix =StringUtils.getServiceKey(map);

    if (methods != null &&methods.size() > 0) {

        for (MethodConfig method : methods){

            appendParameters(map, method,method.getName());

            String retryKey =method.getName() + ".retry";

            if (map.containsKey(retryKey)){

                String retryValue =map.remove(retryKey);

                if ("false".equals(retryValue)) {

                   map.put(method.getName() + ".retries", "0");

                }

            }

            appendAttributes(attributes,method, prifix + "." + method.getName());

            checkAndConvertImplicitConfig(method,map, attributes);

        }

    }

    //attributes通过系统context进行存储.

   StaticContext.getSystemContext().putAll(attributes);

    ref = createProxy(map);

}


基本意思就是集成各种配置,比如类名、方法、版本等等,组成一个map,最终通过createProxy()方法生成代理类,createProxy()方法的代码如下:



@SuppressWarnings({“unchecked”, “rawtypes”,“deprecation”})

private T createProxy(Map<String, String> map) {

    URL tmpUrl = new URL("temp","localhost", 0, map);

    final boolean isJvmRefer;

    if (isInjvm() == null) {

        if (url != null &&url.length() > 0) { //指定URL的情况下,不做本地引用

            isJvmRefer = false;

        } else if(InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {

            //默认情况下如果本地有服务暴露,则引用本地服务.

            isJvmRefer = true;

        } else {

            isJvmRefer = false;

        }

    } else {

        isJvmRefer =isInjvm().booleanValue();

    }



    if (isJvmRefer) {

        URL url = new URL(Constants.LOCAL_PROTOCOL,NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);

        invoker =refprotocol.refer(interfaceClass, url);

        if (logger.isInfoEnabled()) {

            logger.info("Using injvmservice " + interfaceClass.getName());

        }

    } else {

        if (url != null &&url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心URL

            String[] us =Constants.SEMICOLON_SPLIT_PATTERN.split(url);

            if (us != null &&us.length > 0) {

                for (String u : us) {

                    URL url =URL.valueOf(u);

                    if (url.getPath() ==null || url.getPath().length() == 0) {

                        url =url.setPath(interfaceName);

                    }

                    if(Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {

                       urls.add(url.addParameterAndEncoded(Constants.REFER_KEY,StringUtils.toQueryString(map)));

                    } else {

                       urls.add(ClusterUtils.mergeUrl(url, map));

                    }

                }

            }

        } else { // 通过注册中心配置拼装URL

            List<URL> us =loadRegistries(false);

            if (us != null &&us.size() > 0) {

                for (URL u : us) {

                    URL monitorUrl =loadMonitor(u);

                    if (monitorUrl != null){

                       map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));

                    }

                   urls.add(u.addParameterAndEncoded(Constants.REFER_KEY,StringUtils.toQueryString(map)));

                }

            }

            if (urls == null || urls.size()== 0) {

                throw newIllegalStateException("No such any registry to reference " +interfaceName + " on the consumer " + NetUtils.getLocalHost() +" use dubbo version " + Version.getVersion() + ", please config<dubbo:registry address=\"...\" /> to your springconfig.");

            }

        }



        if (urls.size() == 1) {

            invoker =refprotocol.refer(interfaceClass, urls.get(0));

        } else {

            List<Invoker<?>>invokers = new ArrayList<Invoker<?>>();

           URL registryURL =null;

            for (URL url : urls) {

               invokers.add(refprotocol.refer(interfaceClass, url));

                if(Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {

                    registryURL = url; // 用了最后一个registryurl

                }

            }

            if (registryURL != null) { // 有 注册中心协议的URL

                // 对有注册中心的Cluster只用 AvailableCluster

                URL u = registryURL.addParameter(Constants.CLUSTER_KEY,AvailableCluster.NAME);

                invoker = cluster.join(new StaticDirectory(u, invokers));

            } else { // 不是 注册中心的URL

                invoker = cluster.join(new StaticDirectory(invokers));

            }

        }

    }



    Boolean c = check;

    if (c == null && consumer !=null) {

        c = consumer.isCheck();

    }

    if (c == null) {

        c = true; // default true

    }

    if (c && !invoker.isAvailable()){

        throw new IllegalStateException("Failed to check the status of the service " +interfaceName + ". No provider available for the service " + (group== null ? "" : group + "/") + interfaceName + (version ==null ? "" : ":" + version) + " from the url " +invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() +" use dubbo version " + Version.getVersion());

    }

    if (logger.isInfoEnabled()) {

        logger.info("Refer dubboservice " + interfaceClass.getName() + " from url " +invoker.getUrl());

    }

    // 创建服务代理

    return (T)proxyFactory.getProxy(invoker);

}


一开始调用isInjvm()方法判断目标接口是否在本地就有,如果本地就有,直接调用本地的接口。


如果本地没有,就在配置中找有没有用户指定的url,如果指定了就使用用户指定的url提供的接口。


如果没有指定url,则从注册中心中获得目标url列表。


如果urls.size()==1,则直接用这个url获得invoker,这个invoker就是最后用来创建动态代理用的。


当urls.size()>1时,有registryURL属性,如果配置了注册中心协议Protocol,则只用AvailableCluster得到invoker。


cluster.join()方法是用来获得invoker的,cluster属性的定义:



private transient volatile Invoker<?> invoker;


Invoker是个接口,根据配置的不同会使用不同的实现类,比如上面的AvailableCluster,他的join()方法是这样的:



public Invoker join(Directory directory)throws RpcException {

    return new AbstractClusterInvoker<T>(directory) {

        public Result doInvoke(Invocationinvocation, List<Invoker<T>> invokers, LoadBalance loadbalance)throws RpcException {

            for (Invoker<T> invoker :invokers) {

                if (invoker.isAvailable()){

                    returninvoker.invoke(invocation);

                }

            }

            throw new RpcException("No provider available in " + invokers);

        }

    };

}


实际上这个join()方法返回了一个AbstractClusterInvoker对象,并重写了他的doInvoke()方法,这个方法在动态代理实际被调用时会用到。


现在回到createProxy()方法,最后用得到的invoker通过proxyFactory创建动态代理,至此动态代理就创建完了。


 


#### 消费端动态代理部分


当我们在代码中配置好的SequenceService进行远程调用时,实际上调用的是对应Invoker的invoke()方法,invoker是一个接口,对于这个接口的实现大概是这样的:


Invoker 


----AbstractInvoker


----AbstractClusterInvoker


----AbstractProxyInvoker


----DelegateInvoker


----MockClusterInvoker


----MergeableClusterInvoker


----InvokerListenerAdapter


----InvokerListenerAdapter


……


还有很多


AbstractInvoker就是用来远程通信的Invoker


AbstractClusterInvoker是provider是集群时使用Invoker,比AbstractInvoker多了负载均衡,选择provider的过程,最终确定了调用的provider之后还是会调用AbstractInvoker中的invoke()方法。


我们先看AbstractClusterInvoker的invoke()方法:



public Result invoke(final Invocation invocation) throws RpcException {


   checkWhetherDestroyed();


    LoadBalanceloadbalance;


   List<Invoker<T>> invokers = list(invocation);

    if (invokers !=null && invokers.size() > 0) {

        loadbalance =ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()

               .getMethodParameter(invocation.getMethodName(),Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));

    } else {

        loadbalance=ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);

    }

   RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);

    return doInvoke(invocation, invokers, loadbalance);

}

首先判断当前consumer是否已经destory了,然后用list(invocation)方法获得所有的provider信息,获得负载均衡算法LoadBalance,设置同步属性,最后调用doInvoke方法。


AbstractClusterInvoker的doInvoke()方法是个抽象方法:



protected abstract Result doInvoke(Invocation invocation,List<Invoker> invokers,

                                  LoadBalance loadbalance) throws RpcException;

他的子类有很多,比如:


AvailableClusterInvoker 选择第一个可用的provider。 


FailBackClusterInvoker失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作。


FailfastClusterInvoker快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。


FailoverClusterInvoker失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。


FailsafeClusterInvoker失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。


ForkingClusterInvoker并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。


具体使用哪个得看配置


  



我们以之前提到的AvailableClusterInvoker为例,看一下doInvoke()方法:



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);

        }

    }

    throw new RpcException("No provider available in " + invokers);

}

  

就是判断invoker是否可用,可用就直接调用invoker的invoke()方法,实际上调用的还是AbstractInvoker的invoke()方法,如果不是集群就直接调这个方法了,该方法代码如下:



public Result invoke(Invocation inv) throws RpcException {

    if(destroyed.get()) {

        throw new RpcException("Rpc invoker for service " + this + " on consumer" + NetUtils.getLocalHost()

                +" use dubbo version " + Version.getVersion()

                +" is DESTROYED, can not be invoked any more!");

    }

    RpcInvocation invocation = (RpcInvocation) inv;

   invocation.setInvoker(this);

    if (attachment!= null && attachment.size() > 0) {

        invocation.addAttachmentsIfAbsent(attachment);

    }

    Map<String,String> context = RpcContext.getContext().getAttachments();

    if (context !=null) {

       invocation.addAttachmentsIfAbsent(context);

    }

    if(getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY,false)) {

       invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());

    }

   RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);


    try {

        return doInvoke(invocation);

    } catch(InvocationTargetException e) { // biz exception

        Throwablete = 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);

    }

}

还是先判断consumer是否是destory的,其实destroyed是destory的过去分词,不是人家拼错了。


然后经历一堆和AbstractClusterInvoker的invoke一样的参数设置,最后调用doInvoke()方法,而且这个方法在这个Invoker里面也是抽象的。


AbstractInvoker的doInvoke()方法在DubboInvoker类里面有具体实现,这个DubboInvoker是AbstractInvoker的子类,doInvoke()方法如下:



@Override

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);


    ExchangeClient currentClient;

    if(clients.length == 1) {

       currentClient = clients[0];

    } else {

       currentClient = clients[index.getAndIncrement() % clients.length];

    }

    try {

        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) {

           ResponseFuture future = currentClient.request(inv, timeout);

            RpcContext.getContext().setFuture(newFutureAdapter<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);

    }

}

在经过一堆的设置参数(地址、版本)之后,dubbo获得了两个参数,isAsync和isOneway,isAsync为true时代表异步调用,isOneway为true时代表没有返回值。


当isOneway为true时,调用send()方法然后返回一个空的RpcResult,ExchangeClient的send()方法就是用来把消息发给provider的,send()方法的返回值类型是void。


而当isAsync为true时,设置了一个ResponseFuture之后返回一个空的RpcResult


最后的else就是普通的同步调用,不需要设置Future,一直等到provider端返回处理结果,currentClient.request方法负责把请求发出。


 ExchangeClient是个接口,request()方法的实现类在HeaderExchangeClient类中,HeaderExchangeClient的request()方法只有一行,直接调用了HeaderExchangeChannel的request方法,这个request方法如下:



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 + " isclosed!");

    }

    // create request.

    Request req =new Request();

   req.setVersion("2.0.0");

   req.setTwoWay(true);

   req.setData(request);

    DefaultFuture future = new DefaultFuture(channel, req, timeout);

    try {

       channel.send(req);

    } catch(RemotingException e) {

       future.cancel();

        throw e;

    }

    return future;

}

其中的channel就是dubbo集成的Netty的Channel类,负责服务器间消息传输,这个类在dubbo中和netty中都能找到,这里调用了他的send()方法。


Channel的send()方法来自EndPoint接口


Channel接口实现了EndPoint接口


AbstractChannel抽象类实现了Channel接口,然而他的send()方法的功能只是判断当前channel是否已关闭



public void send(Object message, boolean sent) throws RemotingException {

    if (isClosed()){

        throw new RemotingException(this, "Failed to send message "

                +(message == null ? "" : message.getClass().getName()) + ":"+ message

                +", cause: Channel closed. channel: " + getLocalAddress() + "-> " + getRemoteAddress());

    }

}

最后NettyChannel类继承了AbstractChannel类,重写了父类的send()方法,代码如下:



public void send(Object message, boolean sent) throws RemotingException {

   super.send(message, sent);

架构学习资料

准备两个月,面试五分钟,Java中高级岗面试为何越来越难?

准备两个月,面试五分钟,Java中高级岗面试为何越来越难?

准备两个月,面试五分钟,Java中高级岗面试为何越来越难?

准备两个月,面试五分钟,Java中高级岗面试为何越来越难?

准备两个月,面试五分钟,Java中高级岗面试为何越来越难?

由于篇幅限制小编,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

if (isClosed()){

        throw new RemotingException(this, "Failed to send message "

                +(message == null ? "" : message.getClass().getName()) + ":"+ message

                +", cause: Channel closed. channel: " + getLocalAddress() + "-> " + getRemoteAddress());

    }

}

最后NettyChannel类继承了AbstractChannel类,重写了父类的send()方法,代码如下:



public void send(Object message, boolean sent) throws RemotingException {

   super.send(message, sent);

架构学习资料

[外链图片转存中…(img-3QcOT5QR-1714287854077)]

[外链图片转存中…(img-JMxdjmgP-1714287854078)]

[外链图片转存中…(img-1fPveblZ-1714287854078)]

[外链图片转存中…(img-1giF0R88-1714287854079)]

[外链图片转存中…(img-Tau8TYaH-1714287854079)]

由于篇幅限制小编,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值