Bolt提供了四种通信模型,分别是Oneway ,Sync ,Future ,Callback 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