Dubbo Learning Record (19) - Service Call [5] - Service Consumer Call Process Analysis;

Service call process analysis

Common service call code

@Component("demoServiceComponent")
public class DemoServiceComponent implements DemoService {
    
    

    @Reference
    private DemoService demoService;   //

    @Override
    public String sayHello(String name) {
    
    
        return demoService.sayHello(name);  // Invoker
    }
}

Here, the proxy object instance is finally introduced through @Reference. Dubbo uses the Javassist framework to generate a dynamic proxy class for the service interface by default, so we need to decompile the proxy class first to see the source code, and finally call the method sayHello, call handler#invoke, and The implementation class of the handler is the InvokerInvocationhandler type, so you need to look at the InvokerInvocationHandler#invoke method

public class proxy0 implements DC, HelloService, EchoService {
    
    
 
    // 方法数组
    public static Method[] methods;
    private InvocationHandler handler;
 
    public proxy0(InvocationHandler var1) {
    
    
        this.handler = var1;
    }
    public String sayHello(String var1) {
    
    
         // 将参数存储到 Object 数组中
        Object[] var2 = new Object[]{
    
    var1};
 
        // 调用 InvocationHandler 实现类的 invoke 方法得到调用结果
        Object var3 = this.handler.invoke(this, methods[0], var2);
 
        // 返回调用结果
        return (String)var3;
    }
    //....
    }

Service consumer call process

1. InvokerInvocationHandler#invoke

  • Intercept methods defined in the Object class (not overridden by subclasses), such as wait/notify
  • To determine whether the method name is toString, it is to call the toString method directly, without remote calling
  • To judge whether the method name is hashCode, it is to directly call the hashCode method without remote calling
  • To judge whether the method name is equals, it is to call the equals method directly; no remote call is required
  • Neither, invoker.invoke(new RpcInvocation(method, args)) initiates a remote call;
public class InvokerInvocationHandler implements InvocationHandler {
    
    
    private static final Logger logger = LoggerFactory.getLogger(InvokerInvocationHandler.class);
    private final Invoker<?> invoker;

    public InvokerInvocationHandler(Invoker<?> handler) {
    
    
        this.invoker = handler;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
    
    
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
    
    
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
    
    
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
    
    
            return invoker.equals(args[0]);
        }

        // 这里的recreate方法很重要,他会调用AppResponse的recreate方法,
        // 如果AppResponse对象中存在exception信息,则此方法中会throw这个异常
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }
}

RpcInvocation

The properties of this class contain the parameters of the service call;

  • returnType sets the type of return value
  • methodName : the method name to call;
  • parameterTypes: method parameter types;
  • arguments : parameter values
  • invoker : empty, nothing passed;
  • attachments : Some related information about service configuration, timeout, etc.;
public class RpcInvocation implements Invocation, Serializable {
    
    
    public RpcInvocation(Method method, Object[] arguments, Map<String, String> attachment) {
    
    
        this(method.getName(), method.getParameterTypes(), arguments, attachment, null);
        //设置返回值的类型;
        this.returnType = method.getReturnType();
    }
	
	public RpcInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments, Map<String, String> attachments, Invoker<?> invoker) {
    
    
        this.methodName = methodName;
        this.parameterTypes = parameterTypes == null ? new Class<?>[0] : parameterTypes;
        this.arguments = arguments == null ? new Object[0] : arguments;
        this.attachments = attachments == null ? new HashMap<String, String>() : attachments;
        this.invoker = invoker;
    }

}

Therefore, when the server executes the service, the RpcInvocation instance contains the data required for the service execution;

2. MockClusterInvoker#invoke

In InvokerInvocationHandler#invoke, the invoker attribute type is a MockClusterInvoker instance; execute some Mock logic;

  1. Determine whether the mock configuration is empty, if it is empty, the mock logic will not be executed, and the invoke method of the next Invoker will be called;
  2. Is not empty, and starts with force, calls the Mock logic doMockInvoke method, and ends the remote call;
public class MockClusterInvoker<T> implements Invoker<T> {
    
    
    private final Directory<T> directory;
    private final Invoker<T> invoker;
    @Override
    public Result invoke(Invocation invocation) throws RpcException {
    
    
        Result result = null;

        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
        if (value.length() == 0 || "false".equalsIgnoreCase(value)) {
    
    
            //no mock
            result = this.invoker.invoke(invocation);
        } else if (value.startsWith("force")) {
    
    
            //force:direct mock  强制调用Mock逻辑, 直接放回数据;
            result = doMockInvoke(invocation, null);
        } else {
    
    
     		//省略部分代码
        }
        return result;
    }
}

3.1 AbstractClusterInvoker#invoke

Next is the Invoker of the cluster fault tolerance function. The default FailoverClusterInvoker does not define the invoke method inside. It inherits the invoke method defined in the AbstractClusterInvoker class, so it calls the invoke method of the parent class; three things are done
:

  1. Get the attachments in RpcContext and set them to RpcInvocation object;
  2. Call the list method and call the routing chain to obtain the appropriate Invoker instance invokers from the service directory;
  3. Initialize and obtain the load balancing policy loadbalance;
  4. Call the method doIoke implemented by the subclass FailoverClusterInvoker
    @Override
    public Result invoke(final Invocation invocation) throws RpcException {
    
    
        checkWhetherDestroyed();

        // binding attachments into invocation.
        Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
        if (contextAttachments != null && contextAttachments.size() != 0) {
    
    
            ((RpcInvocation) invocation).addAttachments(contextAttachments);
        }

        List<Invoker<T>> invokers = list(invocation);
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        return doInvoke(invocation, invokers, loadbalance);
    }

3.2 FailoverClusterInvoker#doInvoke

  • According to the load balancing strategy, an Invoker is selected for execution;
  • The first normal call, the call fails, will be retried twice, a total of 3 calls;
    workflow:
  1. Get methodName;
  2. Get the number of retries, the default value is 2; the +1 operation will be performed, so the first call needs to be counted;
  3. Call the select method to load balance and select a service;
  4. Call the next invoker#invoke method;
public class FailoverClusterInvoker<T> extends AbstractClusterInvoker<T> {
    
    
    public FailoverClusterInvoker(Directory<T> directory) {
    
    
        super(directory);
    }
    @Override
    @SuppressWarnings({
    
    "unchecked", "rawtypes"})
    public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    
    
        List<Invoker<T>> copyInvokers = invokers;
        String methodName = RpcUtils.getMethodName(invocation);
        int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
        if (len <= 0) {
    
    
            len = 1;
        }
        // retry loop.
        RpcException le = null; // last exception.
        List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
        Set<String> providers = new HashSet<String>(len);
        for (int i = 0; i < len; i++) {
    
    
            if (i > 0) {
    
    
                checkWhetherDestroyed();
                copyInvokers = list(invocation);
                // check again
                checkInvokers(copyInvokers, invocation);
            }
            Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
            invoked.add(invoker);
            RpcContext.getContext().setInvokers((List) invoked);
            try {
    
    
                Result result = invoker.invoke(invocation);
				//省略部分代码
                return result;
            } catch (RpcException e) {
    
    
            } catch (Throwable e) {
    
    
                le = new RpcException(e.getMessage(), e);
            } finally {
    
    
                providers.add(invoker.getUrl().getAddress());
            }
        }
		//异常处理
    }

}

The type of the next Invoker is the RegistryDirectory$InvokerDelegate class, call the InvokerDelegate#invoke method

4 InvokerDelegate#invoke

The InvokerDelegate class does not define the invoke method, which inherits the InvokerWrapper, which defines the invoke method;
the proxy object invoke does nothing;

private static class InvokerDelegate<T> extends InvokerWrapper<T> {
    
    
        private URL providerUrl;

        public InvokerDelegate(Invoker<T> invoker, URL url, URL providerUrl) {
    
    
            super(invoker, url);
            this.providerUrl = providerUrl;
        }

        public URL getProviderUrl() {
    
    
            return providerUrl;
        }
}

public class InvokerWrapper<T> implements Invoker<T> {
    
    

    private final Invoker<T> invoker;
    @Override
    public Result invoke(Invocation invocation) throws RpcException {
    
    
        return invoker.invoke(invocation);
    }
}

5 CallbackRegistrationInvoker#invoke

  • Invoker generated by ProtocolFilterWrapper;
  • The execution filter chain will be called, and the result will be obtained after execution;
  • After getting the result, it will get the listener in the ListenableFilter and execute the onResponse method of the listener
    static class CallbackRegistrationInvoker<T> implements Invoker<T> {
    
    

        private final Invoker<T> filterInvoker;
        private final List<Filter> filters;

        @Override
        public Result invoke(Invocation invocation) throws RpcException {
    
    
            // 执行过滤器链
            Result asyncResult = filterInvoker.invoke(invocation);

            // 过滤器都执行完了之后,回调每个过滤器的onResponse或onError方法
            asyncResult = asyncResult.whenCompleteWithContext((r, t) -> {
    
    
                for (int i = filters.size() - 1; i >= 0; i--) {
    
    
                    Filter filter = filters.get(i);
                    // onResponse callback
                    if (filter instanceof ListenableFilter) {
    
    
                        Filter.Listener listener = ((ListenableFilter) filter).listener();
                        if (listener != null) {
    
    
                            if (t == null) {
    
    
                                listener.onResponse(r, filterInvoker, invocation);
                            } else {
    
    
                                listener.onError(t, filterInvoker, invocation);
                            }
                        }
                    } else {
    
    
                        filter.onResponse(r, filterInvoker, invocation);
                    }
                }
            });
            return asyncResult;
        }
}

5 ConsumerContextFilter#invoke

Set context parameters;

  • local address localAddress;
  • Remote call address remoteAddress
  • remote application name remoteApplicationName;
  • Service parameter attachments;
@Activate(group = CONSUMER, order = -10000)
public class ConsumerContextFilter extends ListenableFilter {
    
    
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    
    
        // 设置RpcContext参数
        RpcContext.getContext()
                .setInvoker(invoker)
                .setInvocation(invocation)
                .setLocalAddress(NetUtils.getLocalHost(), 0)
                .setRemoteAddress(invoker.getUrl().getHost(), invoker.getUrl().getPort())
                .setRemoteApplicationName(invoker.getUrl().getParameter(REMOTE_APPLICATION_KEY))
                .setAttachment(REMOTE_APPLICATION_KEY, invoker.getUrl().getParameter(APPLICATION_KEY));
        if (invocation instanceof RpcInvocation) {
    
    
            ((RpcInvocation) invocation).setInvoker(invoker);
        }
        try {
    
    
            RpcContext.removeServerContext();
            return invoker.invoke(invocation);
        } finally {
    
    
            RpcContext.removeContext();
        }
    }

    static class ConsumerContextListener implements Listener {
    
    
        @Override
        public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
    
    
            RpcContext.getServerContext().setAttachments(appResponse.getAttachments());
        }
		//....
    }
}    

6. FutureFilter#invoke

  1. Call fireInvokeCallback to determine whether there is a callback method, and if so, trigger the callback method
  2. Call the next Invoke#invoke processing;
@Activate(group = CommonConstants.CONSUMER)
public class FutureFilter extends ListenableFilter {
    
    
    @Override
    public Result invoke(final Invoker<?> invoker, final Invocation invocation) throws RpcException {
    
    
        fireInvokeCallback(invoker, invocation);
        // need to configure if there's return value before the invocation in order to help invoker to judge if it's
        // necessary to return future.
        return invoker.invoke(invocation);
    }
}

7. ListenerInvokerWrapper#invoke

Nothing to do, call the next Invoker#invoke method;

public class ListenerInvokerWrapper<T> implements Invoker<T> {
    
    
    private final Invoker<T> invoker;
    private final List<InvokerListener> listeners;
    @Override
    public Result invoke(Invocation invocation) throws RpcException {
    
    
        // AsyncToSyncInvoker
        return invoker.invoke(invocation);
    }

}

8. AsyncToSyncInvoker#invoke

public class AsyncToSyncInvoker<T> implements Invoker<T> {
    
    

    private Invoker<T> invoker;

    public AsyncToSyncInvoker(Invoker<T> invoker) {
    
    
        this.invoker = invoker;
    }

    @Override
    public Class<T> getInterface() {
    
    
        return invoker.getInterface();
    }

    @Override
    public Result invoke(Invocation invocation) throws RpcException {
    
    
        // 异步转同步

        Result asyncResult = invoker.invoke(invocation);  // AsyncRpcResult--->CompletableFuture

        try {
    
    
            // 如果invocation指定是同步的,则阻塞等待结果
            if (InvokeMode.SYNC == ((RpcInvocation) invocation).getInvokeMode()) {
    
    
                asyncResult.get(Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
            }
        } catch (InterruptedException e) {
    
    
  		//..异常处理
        } catch (Throwable e) {
    
    
            throw new RpcException(e.getMessage(), e);
        }
        return asyncResult;
    }
}

9. AbstractInvoker.invoke(invocation)

Mainly call the doInvoke method of DubboInvoker. If there is an exception in the doInvoker method, it will be packaged and packaged into AsyncRpcResult

    @Override
    public Result invoke(Invocation inv) throws RpcException {
    
    
        RpcInvocation invocation = (RpcInvocation) inv;
        invocation.setInvoker(this);

        invocation.setInvokeMode(RpcUtils.getInvokeMode(url, invocation));
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);

        try {
    
    
            return doInvoke(invocation);
        } catch (InvocationTargetException e) {
    
     // biz exception
            Throwable te = e.getTargetException();
            if (te == null) {
    
    
                return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
            } else {
    
    
                if (te instanceof RpcException) {
    
    
                    ((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
                }
                return AsyncRpcResult.newDefaultAsyncResult(null, te, invocation);
            }
        } catch (RpcException e) {
    
    
            if (e.isBiz()) {
    
    
                return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
            } else {
    
    
                throw e;
            }
        } catch (Throwable e) {
    
    
            return AsyncRpcResult.newDefaultAsyncResult(null, e, invocation);
        }
    }

10. DubboInvoker#doInvoke

  • Polling a client from clients to send data, if it is configured not to care about the result, call the send method of ReferenceCountExchangeClient, otherwise call the request method of ReferenceCountExchangeClient;
  • asyncRpcResult.subscribeTo(responseFuture): The responseFuture is bound to the asynchronous RPC result. When the responseFuture gets the return value, it will set the returned result content to the asyncRpcResult asynchronous RPC result
@Override
    protected Result doInvoke(final Invocation invocation) throws Throwable {
    
    
        RpcInvocation inv = (RpcInvocation) invocation;
        final String methodName = RpcUtils.getMethodName(invocation);
        inv.setAttachment(PATH_KEY, getUrl().getPath());
        inv.setAttachment(VERSION_KEY, version);

        // 一个DubboInvoker对象可能并发的同时去调用某个服务
        // 那么单独的一次调用都需要一个单独的client去发送请求
        // 所以这里会去选择使用本次调用该使用哪个client
        ExchangeClient currentClient;
        if (clients.length == 1) {
    
    
            currentClient = clients[0];
        } else {
    
    
            // 轮询使用clients
            currentClient = clients[index.getAndIncrement() % clients.length];
        }

        try {
    
    
            // isOneway为true,表示请求不需要拿结果
            boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
            // 拿当前方法的所配置的超时时间,默认为1000,1秒
            int timeout = getUrl().getMethodPositiveParameter(methodName, TIMEOUT_KEY, DEFAULT_TIMEOUT);
            if (isOneway) {
    
    
                boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
                currentClient.send(inv, isSent);
                // 生成一个默认的值的结果,value=null
                return AsyncRpcResult.newDefaultAsyncResult(invocation);
            } else {
    
    
                AsyncRpcResult asyncRpcResult = new AsyncRpcResult(inv);
                // 异步去请求,得到一个CompletableFuture
                CompletableFuture<Object> responseFuture = currentClient.request(inv, timeout);

                // responseFuture会完成后会调用asyncRpcResult中的方法,这里并不会阻塞,如果要达到阻塞的效果在外层使用asyncRpcResult去控制
                asyncRpcResult.subscribeTo(responseFuture);
                // save for 2.6.x compatibility, for example, TraceFilter in Zipkin uses com.alibaba.xxx.FutureAdapter
                FutureContext.getContext().setCompatibleFuture(responseFuture);
                return asyncRpcResult;
            }
        } 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);
        }
    }

AsyncRpcResult

Asynchronous call result object;

  • Contains the call parameter invocation, and the context attribute;
  • Inherit the AbstractResult abstract class, which inherits the CompletableFuture class, representing an asynchronous class object;
public abstract class AbstractResult extends CompletableFuture<Result> implements Result {
    
    
}

public class AsyncRpcResult extends AbstractResult {
    
    
    private RpcContext storedContext;
    private RpcContext storedServerContext;

    private Invocation invocation;

    public AsyncRpcResult(Invocation invocation) {
    
    
        this.invocation = invocation;
        this.storedContext = RpcContext.getContext();
        this.storedServerContext = RpcContext.getServerContext();
    }
}

11. ReferenceCountExchangeClient#request

Nothing to do, call the client processing of the next layer;

final class ReferenceCountExchangeClient implements ExchangeClient {
    
    
    private final Client client;
    @Override
    public CompletableFuture<Object> request(Object request, int timeout) throws RemotingException {
    
    
        return channel.request(request, timeout);
        }
}

12. HeaderExchangeClient#request

didn't do anything

public class HeaderExchangeClient implements ExchangeClient {
    
    

    private final Client client;
    private final ExchangeChannel channel;

    @Override
    public CompletableFuture<Object> request(Object request, int timeout) throws RemotingException {
    
    
        return channel.request(request, timeout);
        }
}

13.HeaderExchangeChannel#request

Create a request instance req, set the version information of Dubbo, call synchronously, request data request (RpcInvocation instance)
to create a DefaultFuture instance, bind the request ID internally, and put the DefaultFuture instance as value, request ID as key, and put it into the cache FUTURES; After HeaderExchangeHandler receives the returned result, it will take out the DefaultFuture instance from Futures according to the request ID, and then return Response.

    @Override
    public CompletableFuture<Object> request(Object request, int timeout) throws RemotingException {
    
    
        // create request.
        Request req = new Request();
        req.setVersion(Version.getProtocolVersion());
        req.setTwoWay(true);
        req.setData(request);
        DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout);
        try {
    
    
            channel.send(req);
        } catch (RemotingException e) {
    
    
            future.cancel();
            throw e;
        }
        return future;
    }

14. AbstractPeer.send(Object message)

Get the send parameter from the url, the default is false

public abstract class AbstractPeer implements Endpoint, ChannelHandler {
    
    
    private final ChannelHandler handler;
    private volatile URL url;
    @Override
    public void send(Object message) throws RemotingException {
    
    
        send(message, url.getParameter(Constants.SENT_KEY, false));
    }
}

15. AbstractClient#send(Object message, boolean sent)

Did not do anything;

16. NettyChannel#send

  • Call NioSocketChannel#send to send data, call writeAndFlush of NioSocketChannel to send data
  • Then judge if send is true, then block the timeout time specified in the url,
  • Because if send is false, the timeout time will be blocked in HeaderExchangeChannel
  • Determine whether there is an exception in the future, and throw an exception if it exists;
final class NettyChannel extends AbstractChannel {
    
    
    private static final ConcurrentMap<Channel, NettyChannel> CHANNEL_MAP = new ConcurrentHashMap<Channel, NettyChannel>();
    private final Channel channel;

    @Override
    public void send(Object message, boolean sent) throws RemotingException {
    
    
        // whether the channel is closed
        super.send(message, sent);
        boolean success = true;
        int timeout = 0;
        try {
    
    
            ChannelFuture future = channel.writeAndFlush(message);
            if (sent) {
    
    
                // wait timeout ms
                timeout = getUrl().getPositiveParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
				//阻塞一定时间
                success = future.await(timeout);
            }
            Throwable cause = future.cause();
            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");
        }
    }
}

17. NioSocketChannel.writeAndFlush(Object msg)

The bottom-level Netty sends data in a non-blocking manner; it belongs to the content of netty.

Guess you like

Origin blog.csdn.net/yaoyaochengxian/article/details/124569408