Sofabolt-通信模型

624 阅读3分钟

git地址:github.com/sofastack/s…

SOFABolt 是蚂蚁金融服务集团开发的一套基于 Netty 实现的网络通信框架,与Netty相比Sofabolt就像一个自动挡与手动档的汽车的区别,Sofabolt提供复用基础的通信模型:

  • oneway 单向调用
  • invokeSync 同步调用
  • invokeWithFuture 异步future调用
  • invokeWithCallback 异步callback调用

接下来我们仔细看下每个模式的实现

一、oneway

这种模式最简单,单向发送,不等待server的响应,通常用于发送心跳

protected void oneway(final Connection conn, RemotingCommand request) {
        try {
            conn.getChannel().writeAndFlush(request).addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture f) throws Exception {
                    if (!f.isSuccess()) {
                        BaseRemoting.logger.error("Invoke send failed. The address is {}", RemotingUtil.parseRemoteAddress(conn.getChannel()), f.cause());
                    }
                }
            });
        } catch (Exception var4) {
            
        }
    }

二、invokeSync

我们知道netty是一种异步通信框架,这种支持通过countDownLatch来实现异步变同步

com.alipay.remoting.BaseRemoting#invokeSync

protected RemotingCommand invokeSync(final Connection conn, RemotingCommand request, int timeoutMillis) throws RemotingException, InterruptedException {
        final InvokeFuture future = this.createInvokeFuture(request, request.getInvokeContext());
        conn.addInvokeFuture(future);
        final int requestId = request.getId();
        try {
            conn.getChannel().writeAndFlush(request).addListener(new ChannelFutureListener() { // @1
                public void operationComplete(ChannelFuture f) throws Exception {
                    if (!f.isSuccess()) {
                        conn.removeInvokeFuture(requestId)
                        future.putResponse(BaseRemoting.this.commandFactory.createSendFailedResponse(conn.getRemoteAddress(), f.cause())); //@2
                        BaseRemoting.logger.error("Invoke send failed, id={}", requestId, f.cause());
                    }

                }
            });
        } catch (Exception var7) {
            conn.removeInvokeFuture(requestId);
            future.putResponse(this.commandFactory.createSendFailedResponse(conn.getRemoteAddress(), var7)); // @3
            logger.error("Exception caught when sending invocation, id={}", requestId, var7);
        }

        RemotingCommand response = future.waitResponse((long)timeoutMillis); // @4
        if (response == null) {
            conn.removeInvokeFuture(requestId);
            response = this.commandFactory.createTimeoutResponse(conn.getRemoteAddress());
            logger.warn("Wait response, request id={} timeout!", requestId);
        }
        return response;
    }

代码@1: 发送异步请求

代码@2、代码@3: 如果server响应失败或者调用异常,future.putResponse会执行countDownLatch.countDown();

代码@4: 主线程进行countDownLatch.await 等待server端响应

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

com.alipay.remoting.rpc.protocol.RpcResponseProcessor#doProcess

public void doProcess(RemotingContext ctx, RemotingCommand cmd) {
        Connection conn = (Connection)ctx.getChannelContext().channel().attr(Connection.CONNECTION).get();
        InvokeFuture future = conn.removeInvokeFuture(cmd.getId());  // @1 
        ClassLoader oldClassLoader = null;
        try {
            if (future != null) {
                if (future.getAppClassLoader() != null) {
                    oldClassLoader = Thread.currentThread().getContextClassLoader();
                    Thread.currentThread().setContextClassLoader(future.getAppClassLoader());
                }
                future.putResponse(cmd); // @2 
                future.cancelTimeout();

                try {
                    future.executeInvokeCallback(); // @3
                } catch (Exception var10) {
                    logger.error("Exception caught when executing invoke callback, id={}", cmd.getId(), var10);
                }
            } else {
                logger.warn("Cannot find InvokeFuture, maybe already timeout, id={}, from={} ", cmd.getId(), RemotingUtil.parseRemoteAddress(ctx.getChannelContext().channel()));
            }
        } finally {
        }
    }

代码@1: 从invokeFutureMap中从获取InvokeFuture

代码@2: 进行countDownLatch.countDown()

代码@3: 调用callback函数

三、invokeWithFuture

 public RpcResponseFuture invokeWithFuture(Connection conn, Object request, InvokeContext invokeContext, int timeoutMillis) throws RemotingException {
        RemotingCommand requestCommand = this.toRemotingCommand(request, conn, invokeContext, timeoutMillis);
        this.preProcessInvokeContext(invokeContext, requestCommand, conn);
        InvokeFuture future = super.invokeWithFuture(conn, requestCommand, timeoutMillis);
        return new RpcResponseFuture(RemotingUtil.parseRemoteAddress(conn.getChannel()), future);  // @1
}

代码@1: 将future封装成RpcResponseFuture

protected InvokeFuture invokeWithFuture(final Connection conn, RemotingCommand request, int timeoutMillis) {
        InvokeFuture future = this.createInvokeFuture(request, request.getInvokeContext()); 
        conn.addInvokeFuture(future); //@1
        final int requestId = request.getId();
        try {
            Timeout timeout = TimerHolder.getTimer().newTimeout(new TimerTask() { // @2
                public void run(Timeout timeout) throws Exception {
                    InvokeFuture future = conn.removeInvokeFuture(requestId);
                    if (future != null) {                        future.putResponse(BaseRemoting.this.commandFactory.createTimeoutResponse(conn.getRemoteAddress()));
                    }
                }
            }, (long)timeoutMillis, TimeUnit.MILLISECONDS);
            future.addTimeout(timeout);
            conn.getChannel().writeAndFlush(request).addListener(new ChannelFutureListener() { //@3
                public void operationComplete(ChannelFuture cf) throws Exception {
                    if (!cf.isSuccess()) {
                        InvokeFuture f = conn.removeInvokeFuture(requestId);
                        if (f != null) {
                            f.cancelTimeout();                          
                            f.putResponse(BaseRemoting.this.commandFactory.createSendFailedResponse(conn.getRemoteAddress(), cf.cause()));
                        }
                        BaseRemoting.logger.error("Invoke send failed. The address is {}", RemotingUtil.parseRemoteAddress(conn.getChannel()), cf.cause());
                    }

                }
            });
        } catch (Exception var8) {
            InvokeFuture f = conn.removeInvokeFuture(requestId);
            if (f != null) {
                f.cancelTimeout();
                f.putResponse(this.commandFactory.createSendFailedResponse(conn.getRemoteAddress(), var8));
            }

            logger.error("Exception caught when sending invocation. The address is {}", RemotingUtil.parseRemoteAddress(conn.getChannel()), var8);
        }
        return future;
    }

代码@1: 按照request构建InvokeFuture并存放到invokeFutureMap中

代码@2:使用HashedWheelTimer启动延迟任务,超时从invokeFutureMap删除InvokeFuture,并释放countDownLatch

代码@3:发送异步请求

RpcResponseFuture.get()方法就是调用waitResponse阻塞等待直到返回值

public Object get(int timeoutMillis) throws InvokeTimeoutException, RemotingException, InterruptedException {
        this.future.waitResponse((long)timeoutMillis);  
        if (!this.isDone()) {
            throw new InvokeTimeoutException("Future get result timeout!");
        } else {
            ResponseCommand responseCommand = (ResponseCommand)this.future.waitResponse();
            responseCommand.setInvokeContext(this.future.getInvokeContext());
            return RpcResponseResolver.resolveResponseObject(responseCommand, this.addr);
        }
}

四、invokeWithCallback

protected void invokeWithCallback(final Connection conn, RemotingCommand request, InvokeCallback invokeCallback, int timeoutMillis) {
        InvokeFuture future = this.createInvokeFuture(conn, request, request.getInvokeContext(), invokeCallback);
        conn.addInvokeFuture(future);
        final int requestId = request.getId();

        try {
            Timeout timeout = TimerHolder.getTimer().newTimeout(new TimerTask() {
                public void run(Timeout timeout) throws Exception {
                    InvokeFuture future = conn.removeInvokeFuture(requestId);
                    if (future != null) {
                        future.putResponse(BaseRemoting.this.commandFactory.createTimeoutResponse(conn.getRemoteAddress()));
                        future.tryAsyncExecuteInvokeCallbackAbnormally(); // @1
                    }

                }
            }, (long)timeoutMillis, TimeUnit.MILLISECONDS);
            future.addTimeout(timeout);
            conn.getChannel().writeAndFlush(request).addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture cf) throws Exception {
                    if (!cf.isSuccess()) {
                        InvokeFuture f = conn.removeInvokeFuture(requestId);
                        if (f != null) {
                            f.cancelTimeout();
                            f.putResponse(BaseRemoting.this.commandFactory.createSendFailedResponse(conn.getRemoteAddress(), cf.cause()));
                            f.tryAsyncExecuteInvokeCallbackAbnormally();
                        }

                        BaseRemoting.logger.error("Invoke send failed. The address is {}", RemotingUtil.parseRemoteAddress(conn.getChannel()), cf.cause());
                    }

                }
            });
        } catch (Exception var9) {
            InvokeFuture f = conn.removeInvokeFuture(requestId);
            if (f != null) {
                f.cancelTimeout();
                f.putResponse(this.commandFactory.createSendFailedResponse(conn.getRemoteAddress(), var9));
                f.tryAsyncExecuteInvokeCallbackAbnormally();
            }

            logger.error("Exception caught when sending invocation. The address is {}", RemotingUtil.parseRemoteAddress(conn.getChannel()), var9);
        }

    }

代码@1 :超时后future再异步调用callback函数,代码见下面

 public void tryAsyncExecuteInvokeCallbackAbnormally() {
        try {
            Protocol protocol = ProtocolManager.getProtocol(ProtocolCode.fromBytes(new byte[]{this.protocol}));
            if (null != protocol) {
                CommandHandler commandHandler = protocol.getCommandHandler();
                if (null != commandHandler) {
                    ExecutorService executor = commandHandler.getDefaultExecutor();
                    if (null != executor) {
                        executor.execute(new Runnable() {
                            public void run() {
                                ClassLoader oldClassLoader = null;
                                try {
                                    if (DefaultInvokeFuture.this.getAppClassLoader() != null) {
                                        oldClassLoader = Thread.currentThread().getContextClassLoader();
                                        Thread.currentThread().setContextClassLoader(DefaultInvokeFuture.this.getAppClassLoader());
                                    }

                                    DefaultInvokeFuture.this.executeInvokeCallback();
                                } finally {
                                    if (null != oldClassLoader) {
                                        Thread.currentThread().setContextClassLoader(oldClassLoader);
                                    }
                                }
                            }
                        });
                    }
                } else {
                    logger.error("Executor null in commandHandler of protocolCode [{}].", this.protocol);
                }
            } else {
                logger.error("protocolCode [{}] not registered!", this.protocol);
            }
        } catch (Exception var4) {
            logger.error("Exception caught when executing invoke callback abnormally.", var4);
        }
    }

总结:

以上是solt中4通信模型,我们在阿里的开源组件:rocketmq/seata中会看到这样的设计思路。使用者可以不用关心如何实现一个私有协议的细节,直接使用我们内置的 RPC 通信协议。