sofa-bolt源码阅读(3)-通信模型

Bolt提供了四种通信模型,分别是OnewaySyncFutureCallback asynchronous

4.1 Oneway

oneway单向通话

特点

  • 不关心返回值
  • 发送请求就立即返回
com.alipay.remoting.BaseRemoting#oneway
protected void oneway(final Connection conn, final RemotingCommand request) {
    try {
        conn.getChannel().writeAndFlush(request).addListener(new ChannelFutureListener() {

            @Override
            public void operationComplete(ChannelFuture f) throws Exception {
                if (!f.isSuccess()) {
                    logger.error("Invoke send failed. The address is {}",
                        RemotingUtil.parseRemoteAddress(conn.getChannel()), f.cause());
                }
            }

        });
    } catch (Exception e) {
        if (null == conn) {
            logger.error("Conn is null");
        } else {
            logger.error("Exception caught when sending invocation. The address is {}",
                RemotingUtil.parseRemoteAddress(conn.getChannel()), e);
        }
    }
}

4.2 Sync

Sync同步请求

特点

  • 需要获取返回值
  • 如果在超时时间内未返回,触发超时错误
protected RemotingCommand invokeSync(final Connection conn, final RemotingCommand request,
                                     final int timeoutMillis) throws RemotingException,
                                                             InterruptedException {
    final InvokeFuture future = createInvokeFuture(request, request.getInvokeContext());
    conn.addInvokeFuture(future);
    final int requestId = request.getId();
    try {
        conn.getChannel().writeAndFlush(request).addListener(new ChannelFutureListener() {

            @Override
            public void operationComplete(ChannelFuture f) throws Exception {
                if (!f.isSuccess()) {
                    conn.removeInvokeFuture(requestId);
                    future.putResponse(commandFactory.createSendFailedResponse(
                        conn.getRemoteAddress(), f.cause()));
                    logger.error("Invoke send failed, id={}", requestId, f.cause());
                }
            }

        });
    } catch (Exception e) {
        conn.removeInvokeFuture(requestId);
        future.putResponse(commandFactory.createSendFailedResponse(conn.getRemoteAddress(), e));
        logger.error("Exception caught when sending invocation, id={}", requestId, e);
    }
    RemotingCommand response = future.waitResponse(timeoutMillis);

    if (response == null) {
        conn.removeInvokeFuture(requestId);
        response = this.commandFactory.createTimeoutResponse(conn.getRemoteAddress());
        logger.warn("Wait response, request id={} timeout!", requestId);
    }

    return response;
}

上面的代码是客户端发送请求的代码,InvokeFuture的实现类是DefaultInvokeFuture,内部用CountDownLatch来实现同步,初始化时CountDownLatch默认值是1.

private final CountDownLatch     countDownLatch          = new CountDownLatch(1);

当调用putResponse时,countDownLatch计数减1

@Override
public void putResponse(RemotingCommand response) {
    this.responseCommand = (ResponseCommand) response;
    this.countDownLatch.countDown();
}

然后触发waitResponse返回

@Override
public ResponseCommand waitResponse(long timeoutMillis) throws InterruptedException {
    this.countDownLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
    return this.responseCommand;
}

服务器正常返回了,在哪里触发的putResponse呢?

在RpcResponseProcessor类中可以追踪到putResponse

com.alipay.remoting.rpc.protocol.RpcResponseProcessor#doProcess
public void doProcess(RemotingContext ctx, RemotingCommand cmd) {

    Connection conn = ctx.getChannelContext().channel().attr(Connection.CONNECTION).get();
    InvokeFuture future = conn.removeInvokeFuture(cmd.getId());
    ClassLoader oldClassLoader = null;
    try {
        if (future != null) {
            if (future.getAppClassLoader() != null) {
                oldClassLoader = Thread.currentThread().getContextClassLoader();
                Thread.currentThread().setContextClassLoader(future.getAppClassLoader());
            }
            future.putResponse(cmd);
            future.cancelTimeout();
            try {
                future.executeInvokeCallback();
            } catch (Exception e) {
                logger.error("Exception caught when executing invoke callback, id={}",
                    cmd.getId(), e);
            }
        } else {
            logger
                .warn("Cannot find InvokeFuture, maybe already timeout, id={}, from={} ",
                    cmd.getId(),
                    RemotingUtil.parseRemoteAddress(ctx.getChannelContext().channel()));
        }
    } finally {
        if (null != oldClassLoader) {
            Thread.currentThread().setContextClassLoader(oldClassLoader);
        }
    }

}

4.3 Future

Future异步请求,与同步请求不同,Futrue直接返回InvokeFuture对象,让线程继续执行。直到调用InvokeFuture.get方法时,如果response已经返回,则可以直接获取结果,否则才会阻塞线程。

protected InvokeFuture invokeWithFuture(final Connection conn, final RemotingCommand request, final int timeoutMillis) {
    final InvokeFuture future = createInvokeFuture(request, request.getInvokeContext());
    conn.addInvokeFuture(future);
    final int requestId = request.getId();
    try {
        Timeout timeout = TimerHolder.getTimer().newTimeout(new TimerTask() {
            @Override
            public void run(Timeout timeout) throws Exception {
                InvokeFuture future = conn.removeInvokeFuture(requestId);
                if (future != null) {
                    future.putResponse(commandFactory.createTimeoutResponse(conn
                        .getRemoteAddress()));
                }
            }

        }, timeoutMillis, TimeUnit.MILLISECONDS);
        future.addTimeout(timeout);

        conn.getChannel().writeAndFlush(request).addListener(new ChannelFutureListener() {

            @Override
            public void operationComplete(ChannelFuture cf) throws Exception {
                if (!cf.isSuccess()) {
                    InvokeFuture f = conn.removeInvokeFuture(requestId);
                    if (f != null) {
                        f.cancelTimeout();
                        f.putResponse(commandFactory.createSendFailedResponse(
                            conn.getRemoteAddress(), cf.cause()));
                    }
                    logger.error("Invoke send failed. The address is {}",
                        RemotingUtil.parseRemoteAddress(conn.getChannel()), cf.cause());
                }
            }

        });
    } catch (Exception e) {
        InvokeFuture f = conn.removeInvokeFuture(requestId);
        if (f != null) {
            f.cancelTimeout();
            f.putResponse(commandFactory.createSendFailedResponse(conn.getRemoteAddress(), e));
        }
        logger.error("Exception caught when sending invocation. The address is {}",
            RemotingUtil.parseRemoteAddress(conn.getChannel()), e);
    }
    return future;
}

4.4 Callback asynchronous

CallBack带回调函数的异步请求,与Future相比可以传入回调函数

protected void invokeWithCallback(final Connection conn, final RemotingCommand request,
                                  final InvokeCallback invokeCallback, final int timeoutMillis) {
    final InvokeFuture future = createInvokeFuture(conn, request, request.getInvokeContext(),
        invokeCallback);
    conn.addInvokeFuture(future);
    final int requestId = request.getId();
    try {
        Timeout timeout = TimerHolder.getTimer().newTimeout(new TimerTask() {
            @Override
            public void run(Timeout timeout) throws Exception {
                InvokeFuture future = conn.removeInvokeFuture(requestId);
                if (future != null) {
                    future.putResponse(commandFactory.createTimeoutResponse(conn
                        .getRemoteAddress()));
                    future.tryAsyncExecuteInvokeCallbackAbnormally();
                }
            }

        }, timeoutMillis, TimeUnit.MILLISECONDS);
        future.addTimeout(timeout);
        conn.getChannel().writeAndFlush(request).addListener(new ChannelFutureListener() {

            @Override
            public void operationComplete(ChannelFuture cf) throws Exception {
                if (!cf.isSuccess()) {
                    InvokeFuture f = conn.removeInvokeFuture(requestId);
                    if (f != null) {
                        f.cancelTimeout();
                        f.putResponse(commandFactory.createSendFailedResponse(
                            conn.getRemoteAddress(), cf.cause()));
                        f.tryAsyncExecuteInvokeCallbackAbnormally();
                    }
                    logger.error("Invoke send failed. The address is {}",
                        RemotingUtil.parseRemoteAddress(conn.getChannel()), cf.cause());
                }
            }

        });
    } catch (Exception e) {
        InvokeFuture f = conn.removeInvokeFuture(requestId);
        if (f != null) {
            f.cancelTimeout();
            f.putResponse(commandFactory.createSendFailedResponse(conn.getRemoteAddress(), e));
            f.tryAsyncExecuteInvokeCallbackAbnormally();
        }
        logger.error("Exception caught when sending invocation. The address is {}",
            RemotingUtil.parseRemoteAddress(conn.getChannel()), e);
    }
}

回调函数的执行用了观察者模式,InvokeCallbackListener根据reponse的结果执行回调函数的onResponse或onException方法


public void run() {
    InvokeCallback callback = future.getInvokeCallback();
    // a lot of try-catches to protect thread pool
    ResponseCommand response = null;

    try {
        response = (ResponseCommand) future.waitResponse(0);
    } catch (InterruptedException e) {
        String msg = "Exception caught when getting response from InvokeFuture. The address is "
                     + this.remoteAddress;
        logger.error(msg, e);
    }
    if (response == null || response.getResponseStatus() != ResponseStatus.SUCCESS) {
        try {
            Exception e;
            if (response == null) {
                e = new InvokeException("Exception caught in invocation. The address is "
                                        + this.remoteAddress + " responseStatus:"
                                        + ResponseStatus.UNKNOWN, future.getCause());
            } else {
                response.setInvokeContext(future.getInvokeContext());
                switch (response.getResponseStatus()) {
                    case TIMEOUT:
                        e = new InvokeTimeoutException(
                            "Invoke timeout when invoke with callback.The address is "
                                    + this.remoteAddress);
                        break;
                    case CONNECTION_CLOSED:
                        e = new ConnectionClosedException(
                            "Connection closed when invoke with callback.The address is "
                                    + this.remoteAddress);
                        break;
                    case SERVER_THREADPOOL_BUSY:
                        e = new InvokeServerBusyException(
                            "Server thread pool busy when invoke with callback.The address is "
                                    + this.remoteAddress);
                        break;
                    case SERVER_EXCEPTION:
                        String msg = "Server exception when invoke with callback.Please check the server log! The address is "
                                     + this.remoteAddress;
                        RpcResponseCommand resp = (RpcResponseCommand) response;
                        resp.deserialize();
                        Object ex = resp.getResponseObject();
                        if (ex instanceof Throwable) {
                            e = new InvokeServerException(msg, (Throwable) ex);
                        } else {
                            e = new InvokeServerException(msg);
                        }
                        break;
                    default:
                        e = new InvokeException(
                            "Exception caught in invocation. The address is "
                                    + this.remoteAddress + " responseStatus:"
                                    + response.getResponseStatus(), future.getCause());

                }
            }
            callback.onException(e);
        } catch (Throwable e) {
            logger
                .error(
                    "Exception occurred in user defined InvokeCallback#onException() logic, The address is {}",
                    this.remoteAddress, e);
        }
    } else {
        ClassLoader oldClassLoader = null;
        try {
            if (future.getAppClassLoader() != null) {
                oldClassLoader = Thread.currentThread().getContextClassLoader();
                Thread.currentThread().setContextClassLoader(future.getAppClassLoader());
            }
            response.setInvokeContext(future.getInvokeContext());
            RpcResponseCommand rpcResponse = (RpcResponseCommand) response;
            response.deserialize();
            try {
                callback.onResponse(rpcResponse.getResponseObject());
            } catch (Throwable e) {
                logger
                    .error(
                        "Exception occurred in user defined InvokeCallback#onResponse() logic.",
                        e);
            }
        } catch (CodecException e) {
            logger
                .error(
                    "CodecException caught on when deserialize response in RpcInvokeCallbackListener. The address is {}.",
                    this.remoteAddress, e);
        } catch (Throwable e) {
            logger.error(
                "Exception caught in RpcInvokeCallbackListener. The address is {}",
                this.remoteAddress, e);
        } finally {
            if (oldClassLoader != null) {
                Thread.currentThread().setContextClassLoader(oldClassLoader);
            }
        }
    } // enf of else
} // end of run

猜你喜欢

转载自www.cnblogs.com/huiyao/p/12417874.html