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 通信协议。