五Dubbo服务引用源码分析--4网络处理-4.1consumer端发送请求

五Dubbo服务引用源码分析--4网络处理-4.1consumer端发送请求

5.3 服务请求网络处理(connect、received)

image-20230313132935868

本章节中,主要分析的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消息(补充)

image-20230313133001358

provider端在处理connect连接请求时,没有什么处理,不再具体跟进代码分析。

5.3.2 consumer端发送请求

image-20230313133021570

此时,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&register.ip=192.168.0.101&remote.timestamp=1672565419921&side=consumer&timestamp=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

image-20230104153608302

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

posted @ 2023-03-13 14:50  LeasonXue  阅读(32)  评论(0编辑  收藏  举报