• consumer发送请求,接收响应


    一般情况,consumer发送请求时,创建一个DefaultFuture对象,然后阻塞并等待响应。DefaultFuture类,封装了请求和响应:

    // 省略其他代码
    public class DefaultFuture implements ResponseFuture {
        private final Lock                           lock = new ReentrantLock();
        private final Condition                      done = lock.newCondition();
        //waiting map
        private static final Map<Long, DefaultFuture> FUTURES 
                    = new ConcurrentHashMap<Long, DefaultFuture>();
        private final Request                         request;
        private volatile Response                     response;
    }

    1. consumer发送请求,阻塞等待响应。

    调用代码:

    HelloService helloService = (HelloService) appCtx.getBean("hello");
    String hello = helloService.sayHello();

    调用代理对象的sayHello方法,代理类proxy0由javassist动态生成,代码大致如下:

    class com.alibaba.dubbo.common.bytecode.proxy0 implements
        com.alibaba.dubbo.rpc.service.EchoService, com.zhang.HelloService {
        public <init>(java.lang.reflect.InvocationHandler arg0){
            handler=$1;
        }
        public static java.lang.reflect.Method[] methods;
        private java.lang.reflect.InvocationHandler handler;
        
        public java.lang.String sayHello(){
            Object[] args = new Object[0];
            //handler是InvokerInvocationHandler对象
            Object ret = handler.invoke(this, methods[0], args);
            return (java.lang.String)ret;
        }
        public java.lang.Object $echo(java.lang.Object arg0){
            Object[] args = new Object[1];
            args[0] = ($w)$1;
            Object ret = handler.invoke(this, methods[1], args);
            return (java.lang.Object)ret;
        }
    }

    进InvokerInvocationHandler

    //InvokerInvocationHandler类
    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]);
        }
        //invoker是MockClusterInvoker
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }

    跳过中间步骤,进到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);
        
        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(new FutureAdapter<Object>(future));
                return new RpcResult();
            } else {
    //currentClient对象是 ReferenceCountExchangeClient/HeaderExchangeClient/HeaderExchangeChannel //拆分来看:ResponseFuture responseFuture = currentClient.request(inv, timeout); // responseFuture.get(); 调用get导致调用线程阻塞 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); } }

    进HeaderExchangeChannel

    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!"); } // 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; }

    进DefaultFuture

    public Object get() throws RemotingException {
        return get(timeout);
    }
    
    public Object get(int timeout) throws RemotingException {
        if (timeout <= 0) {
            timeout = Constants.DEFAULT_TIMEOUT;
        }
        if (! isDone()) {
            long start = System.currentTimeMillis();
            lock.lock();
            try {
                while (!isDone()) {
                    //调用线程等待直到超时,或者被DubboClientHandler线程唤醒
                    done.await(timeout, TimeUnit.MILLISECONDS);
                    if (isDone() || System.currentTimeMillis() - start > timeout) {
                        break;
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
            if (! isDone()) {
                throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
            }
        }
        return returnFromResponse();
    }
    
    private Object returnFromResponse() throws RemotingException {
        Response res = response;
        if (res == null) {
            throw new IllegalStateException("response cannot be null");
        }
        if (res.getStatus() == Response.OK) {
            return res.getResult();
        }
        if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
            throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, 
        channel, res.getErrorMessage()); }
    throw new RemotingException(channel, res.getErrorMessage()); }

    2. 在DubboClientHandler线程中,consumer接收响应,唤醒调用线程。

    从ChannelEventRunnable进

    //ChannelEventRunnable类
    public void run() {
        //省略其他代码
        switch (state) {
            case RECEIVED:
                handler.received(channel, message);break;
            default:
                logger.warn("unknown state: " + state + ", message is " + message);
        }
    }

    进DecodeHandler

    public void received(Channel channel, Object message) throws RemotingException {
        if (message instanceof Decodeable) {
            decode(message);
        }
    
        if (message instanceof Request) {
            decode(((Request)message).getData());
        }
    
        if (message instanceof Response) {
            decode( ((Response)message).getResult());
        }
    
        handler.received(channel, message);
    }

    进HeaderExchangeHandler

    public void received(Channel channel, Object message) throws RemotingException {
        channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
        ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
        try {
            if (message instanceof Request) {
                // handle request.
                Request request = (Request) message;
                if (request.isEvent()) {
                    handlerEvent(channel, request);
                } else {
                    if (request.isTwoWay()) {
                        Response response = handleRequest(exchangeChannel, request);
                        channel.send(response);
                    } else {
                        handler.received(exchangeChannel, request.getData());
                    }
                }
            } else if (message instanceof Response) {
                handleResponse(channel, (Response) message);
            } else if (message instanceof String) {
                if (isClientSide(channel)) {
                    Exception e = new Exception("Dubbo client can not supported string message: "
                   + message + " in channel: " + channel + ", url: " + channel.getUrl()); logger.error(e.getMessage(), e); } else { String echo = handler.telnet(channel, (String) message); if (echo != null && echo.length() > 0) { channel.send(echo); } } } else { handler.received(exchangeChannel, message); } } finally { HeaderExchangeChannel.removeChannelIfDisconnected(channel); } } static void handleResponse(Channel channel, Response response) throws RemotingException { if (response != null && !response.isHeartbeat()) { DefaultFuture.received(channel, response); } }

    进DefaultFuture

    public static void received(Channel channel, Response response) {
        try {
            //收到响应删除future
            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());
        }
    }
    
    private void doReceived(Response res) {
        lock.lock();
        try {
            //给响应赋值
            response = res;
            if (done != null) {
                //DubboClientHandler线程唤醒调用线程
                done.signal();
            }
        } finally {
            lock.unlock();
        }
        if (callback != null) {
            invokeCallback(callback);
        }
    }

    可以看到实际是两个线程在通信,main线程发送调用请求,并阻塞,DubboClientHandler线程接收到响应,并唤醒主线程。

    DubboClientHandler是一个线程池,它执行工作队列中的任务(即ChannelEventRunnable对象)。

    又是谁把ChannelEventRunnable对象放到DubboClientHandler线程池的工作队列中的呢?答案是New I/O client worker

    //AllChannelHandler类
    public void received(Channel channel, Object message) throws RemotingException {
        //DubboClientHandler线程池
        ExecutorService cexecutor = getExecutorService();
        try {
            cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
        } catch (Throwable t) {
            throw new ExecutionException(message, channel, getClass() + 
                      " error when process received event .", t);
        }
    }

    如果一个方法没有返回值,声明为:public void sayHello();
    但配置不是oneway,那么cosumer还是会返回RpcResult,但是RpcResult中的内容为空。

  • 相关阅读:
    UnknownHostException: xxx,使用nacos远程调用服务(负载均衡)报错
    Failed to bind properties under 'logging.level' to java.util.Map<java.lang.String, java.lang.String>日志级别的问题
    JS 如何返回到父页面?多重跳转之后返回到初始页(父页面)?或者说返回父页面的父页面?
    绝对路径${pageContext.request.contextPath}用法及其与web.xml中Servlet的url-pattern匹配过程
    VS在release模式下进行调试
    visual studio 运行找不到dll库
    C++ 字符串格式化
    SWIG使用遇到的问题
    其他技术---域名中转
    mongoDB增删改查(命令行操作数据库)
  • 原文地址:https://www.cnblogs.com/allenwas3/p/8184466.html
Copyright © 2020-2023  润新知