SOFABolt 的超時(shí)分為兩種:連接超時(shí)和調(diào)用超時(shí)。
- 連接超時(shí)
- 僅客戶端可設(shè)置诅岩,因?yàn)橹挥锌蛻舳藭?huì)建連
- 連接超時(shí)時(shí)間的設(shè)置只作用于建連的時(shí)候讳苦,有兩種方式可以指定 -
addr 拼接屬性參數(shù)
或者url 設(shè)置超時(shí)時(shí)間屬性
- 連接超時(shí)機(jī)制底層實(shí)際上使用的 Netty 的超時(shí)參數(shù)設(shè)置方式
- 調(diào)用超時(shí):調(diào)用端無(wú)論是 RpcClient 還是 RpcServer带膜,除了 oneway 模式下,剩余的三種調(diào)用模式 sync / future / callback 的方法都提供了一個(gè)必填參數(shù) int timeoutMillis鸳谜,該參數(shù)用來(lái)指定調(diào)用超時(shí)時(shí)間
調(diào)用超時(shí)時(shí)間對(duì)于同步調(diào)用和異步調(diào)用不同
- sync:使用
CountDownLatch # boolean await(long timeout, TimeUnit unit)
實(shí)現(xiàn)當(dāng)前線程的實(shí)現(xiàn)阻塞等待- future 和 callback:使用 Netty 的 HashedWheelTimer 實(shí)現(xiàn) -
在發(fā)出請(qǐng)求的時(shí)候創(chuàng)建 timeoutMillis 時(shí)間的超時(shí)任務(wù) task膝藕,當(dāng)在 timeoutMillis 時(shí)間內(nèi)沒(méi)有返回響應(yīng),則執(zhí)行 task 中的內(nèi)容(構(gòu)造超時(shí)響應(yīng)咐扭,返回給調(diào)用程序)芭挽;否則,取消 task
fail-fast 機(jī)制
- 僅用在被調(diào)用端草描,即請(qǐng)求的處理端
- fail-fast 機(jī)制指的是在序列化全部?jī)?nèi)容之前览绿,會(huì)做一個(gè)判斷策严,如果處理當(dāng)前請(qǐng)求的 UserProcessor 開(kāi)啟了 fail-fast 功能穗慕,并且此時(shí)已經(jīng)超時(shí),并且不是 oneway 模式(oneway 沒(méi)有超時(shí)概念)妻导,則直接丟棄該請(qǐng)求逛绵,不再進(jìn)行后續(xù)的序列化和業(yè)務(wù)邏輯處理操作
- fail-fast 機(jī)制是由 UserProcessor#timeoutDiscard() 指定的,如果返回 true倔韭,則打開(kāi) fail-fast术浪,默認(rèn)情況下 fail-fast 機(jī)制是打開(kāi)的;如果想關(guān)閉該功能寿酌,手動(dòng)覆蓋 UserProcessor#timeoutDiscard() 方法即可胰苏,直接返回 false,之后是否超時(shí)就從 DefaultBizContext#isRequestTimeout() 進(jìn)行判斷
一醇疼、連接超時(shí)機(jī)制
默認(rèn)情況下硕并,連接超時(shí)時(shí)間由
RpcConfigs.CONNECT_TIMEOUT_KEY
來(lái)指定,默認(rèn)為 1000ms秧荆,可以通過(guò)在 addr 上拼接參數(shù)或者在 url 上設(shè)置值來(lái)指定連接超時(shí)時(shí)間倔毙。
使用姿勢(shì)
// addr 拼接方式
String addr = "127.0.0.1:8888?_CONNECTTIMEOUT=3000";
// url 設(shè)置值的方式
Url url = new Url("127.0.0.1",8888);
url.setConnNum(1);
url.setConnectTimeout(3000);
源碼分析
以 url 方式為例,addr 只是將 _CONNECTTIMEOUT=3000 properties 屬性設(shè)置到了url.connectTimeout 中乙濒。后續(xù)還是使用 url 方式進(jìn)行調(diào)用陕赃。在調(diào)用的過(guò)程中會(huì)獲取或者創(chuàng)建連接,連接超時(shí)作用于創(chuàng)建連接颁股。
public abstract class AbstractConnectionFactory implements ConnectionFactory {
@Override
public Connection createConnection(Url url) throws Exception {
// 創(chuàng)建連接
Channel channel = doCreateConnection(url.getIp(), url.getPort(), url.getConnectTimeout());
...
return conn;
}
protected Channel doCreateConnection(String targetIP, int targetPort, int connectTimeout) {
// 預(yù)處理 connectTimeout么库,最小為 1000
connectTimeout = Math.max(connectTimeout, 1000);
// 設(shè)置 Netty 的連接超時(shí)時(shí)間屬性
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout);
// 進(jìn)行連接
ChannelFuture future = bootstrap.connect(new InetSocketAddress(targetIP, targetPort));
future.awaitUninterruptibly();
...
return future.channel();
}
}
二、調(diào)用超時(shí)機(jī)制(調(diào)用端)
調(diào)用超時(shí)時(shí)間對(duì)于同步調(diào)用和異步調(diào)用不同
- sync:使用 CountDownLatch # boolean await(long timeout, TimeUnit unit) 實(shí)現(xiàn)當(dāng)前線程的實(shí)現(xiàn)阻塞等待
- future 和 callback:使用 Netty 的 HashedWheelTimer 實(shí)現(xiàn)
調(diào)用端無(wú)論是 RpcClient 還是 RpcServer甘有,除了 oneway 模式下廊散,剩余的三種調(diào)用模式 sync / future / callback 的方法都提供了一個(gè)必填參數(shù) int timeoutMillis,該參數(shù)用來(lái)指定調(diào)用超時(shí)時(shí)間
Object invokeSync(String addr, Object request, int timeoutMillis)
RpcResponseFuture invokeWithFuture(String addr, Object request, int timeoutMillis)
void invokeWithCallback(String addr, Object request, InvokeCallback invokeCallback, int timeoutMillis)
同步方式
============================ class RpcRemoting extends BaseRemoting ============================
public Object invokeSync(Connection conn, Object request, InvokeContext invokeContext, int timeoutMillis) {
// 創(chuàng)建請(qǐng)求:設(shè)置 int timeout = timeoutMillis(默認(rèn)為-1梧疲,表示永不超時(shí))
// 該 timeout 參數(shù)會(huì)用在被調(diào)用端判斷是否超時(shí)允睹,進(jìn)而做 fail-fast 操作
RemotingCommand requestCommand = toRemotingCommand(request, conn, invokeContext, timeoutMillis);
...
// 發(fā)起請(qǐng)求
ResponseCommand responseCommand = (ResponseCommand) super.invokeSync(conn, requestCommand, timeoutMillis);
...
// 解析響應(yīng)
Object responseObject = RpcResponseResolver.resolveResponseObject(responseCommand, RemotingUtil.parseRemoteAddress(conn.getChannel()));
return responseObject;
}
看下解析響應(yīng)流程
public static Object resolveResponseObject(ResponseCommand responseCommand, String addr) throws RemotingException {
... 預(yù)處理響應(yīng)运准,服務(wù)服務(wù)端返回異常信息,直接拋出異常
preProcess(responseCommand, addr);
if (responseCommand.getResponseStatus() == ResponseStatus.SUCCESS) {
return toResponseObject(responseCommand);
} else {
...
}
}
private static void preProcess(ResponseCommand responseCommand, String addr) throws RemotingException {
RemotingException e = null;
// responseCommand == null 超時(shí)
if (responseCommand == null) {
e = new InvokeTimeoutException(msg);
} else {
switch (responseCommand.getResponseStatus()) {
case TIMEOUT:
e = new InvokeTimeoutException(msg);
break;
...
}
}
// 直接拋出超時(shí)異常
if (null != e) {
throw e;
}
}
============================ BaseRemoting ============================
protected RemotingCommand invokeSync(Connection conn, RemotingCommand request, int timeoutMillis) {
// 創(chuàng)建 InvokeFuture
final InvokeFuture future = createInvokeFuture(request, request.getInvokeContext());
// 添加 InvokeFuture 到 Connection
conn.addInvokeFuture(future);
// 使用 Netty 發(fā)送請(qǐng)求
conn.getChannel().writeAndFlush(request)
// 等待 timeoutMillis缭受,如果超時(shí)胁澳,不等待結(jié)果直接返回 response,此時(shí)的 response == null米者,實(shí)際代碼如下
// public ResponseCommand waitResponse(long timeoutMillis) throws InterruptedException {
// // 如果超時(shí)韭畸,知己返回false,this.responseCommand 直接返回 null
// this.countDownLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
// return this.responseCommand;
// }
RemotingCommand response = future.waitResponse(timeoutMillis);
// response == null 客戶端自己創(chuàng)建超時(shí)響應(yīng)
if (response == null) {
conn.removeInvokeFuture(request.getId());
response = this.commandFactory.createTimeoutResponse(conn.getRemoteAddress());
}
return response;
}
注意:RpcRequestCommand 中的 timeout 參數(shù)會(huì)用在被調(diào)用端判斷是否超時(shí)蔓搞,進(jìn)而做 fail-fast 操作
HashWheelTimer 簡(jiǎn)介
future 和 callback 異步模式的超時(shí)機(jī)制都使用了 Netty 的 HashWheelTimer 機(jī)制胰丁。
Netty 的 HashWheelTimer 是一個(gè)環(huán)形結(jié)構(gòu),假設(shè)有 8 個(gè)格子(格子數(shù)可以通過(guò) ticksPerWheel 構(gòu)造器參數(shù)指定喂分,默認(rèn)為 512)锦庸,一個(gè)格子代表一段時(shí)間(越短 Timer 精度越高,每個(gè)格子代表的時(shí)間可以通過(guò) tickDuration 指定蒲祈,默認(rèn)為 100ms甘萧,SOFABolt 指定為 10ms)。
以上圖為例梆掸,假設(shè)一個(gè)格子是 1s扬卷,則整個(gè) wheel 能表示的時(shí)間段為 8s,假如當(dāng)前指針指向 2酸钦,此時(shí)需要調(diào)度一個(gè) 3s 后執(zhí)行的任務(wù)怪得,顯然應(yīng)該加入到 (2+3=5) 的方格中的鏈表中,并且 round = 0卑硫,指針再走 3 次就可以執(zhí)行了徒恋;如果任務(wù)要在 10s 后執(zhí)行,應(yīng)該等指針走完一個(gè) round 零 2 格再執(zhí)行拔恰,因此應(yīng)放入4((2+10)%8=4)的方格中的鏈表中因谎,其 round 設(shè)為 1。檢查到期任務(wù)時(shí)只執(zhí)行 round 為 0 的颜懊,格子上其他任務(wù)的 round 減 1财岔。
Netty 中有一條線程控制 HashWheelTimer 中的指針每隔 tickDuration 移動(dòng)到下一個(gè)格子,執(zhí)行其中 round 為 0 并且不在 cancel 隊(duì)列的任務(wù)河爹,該格子上的其他任務(wù)的 round 減 1匠璧。
當(dāng)取消一個(gè)超時(shí)任務(wù)時(shí),直接將該任務(wù)添加到 cancel 隊(duì)列咸这,就不會(huì)被執(zhí)行了夷恍。
最后看看 HashWheelTimer 在 SOFABolt 中的最佳實(shí)踐。
public class TimerHolder {
// 每格 10 ms
private final static long defaultTickDuration = 10;
// HashedWheelTimer 單例
private static class DefaultInstance {
static final Timer INSTANCE = new HashedWheelTimer(new NamedThreadFactory( "DefaultTimer" + defaultTickDuration, true), defaultTickDuration, TimeUnit.MILLISECONDS);
}
private TimerHolder() {
}
public static Timer getTimer() {
return DefaultInstance.INSTANCE;
}
}
// 使用見(jiàn)下邊的分析
Timeout timeout = TimerHolder.getTimer().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
InvokeFuture future = conn.removeInvokeFuture(request.getId());
future.putResponse(commandFactory.createTimeoutResponse(conn.getRemoteAddress()));
future.tryAsyncExecuteInvokeCallbackAbnormally();
}
}, timeoutMillis, TimeUnit.MILLISECONDS);
異步方式(future 模式)
future 的阻塞點(diǎn)在 RpcResponseFuture.get()操作上媳维,實(shí)際上同同步一樣酿雪,底層也是阻塞在 countDownLatch.await() 上
============================ class RpcRemoting extends BaseRemoting ==================
public RpcResponseFuture invokeWithFuture(Connection conn, Object request, InvokeContext invokeContext, int timeoutMillis) {
// 創(chuàng)建請(qǐng)求:設(shè)置 int timeout = timeoutMillis(默認(rèn)為-1遏暴,表示永不超時(shí))
// 該 timeout 參數(shù)會(huì)用在被調(diào)用端判斷是否超時(shí),進(jìn)而做 fail-fast 操作
RemotingCommand requestCommand = toRemotingCommand(request, conn, invokeContext, timeoutMillis);
...
// 發(fā)起請(qǐng)求
InvokeFuture future = super.invokeWithFuture(conn, requestCommand, timeoutMillis);
// 直接返回響應(yīng)指黎,在 get 的時(shí)候朋凉,依然使用 RpcResponseResolver.resolveResponseObject 解析響應(yīng),如果超時(shí)醋安,直接拋出超時(shí)異常
return new RpcResponseFuture(RemotingUtil.parseRemoteAddress(conn.getChannel()), future);
}
============================ BaseRemoting ============================
protected InvokeFuture invokeWithFuture(Connection conn, RemotingCommand request, int timeoutMillis) {
// 創(chuàng)建 InvokeFuture
final InvokeFuture future = createInvokeFuture(request, request.getInvokeContext());
// 添加 InvokeFuture 到 Connection
conn.addInvokeFuture(future);
try {
// 創(chuàng)建超時(shí)任務(wù)杂彭,添加到 Netty 的 hashWheel 中,在 timeoutMillis 之后如果該超時(shí)任務(wù)沒(méi)有被取消吓揪,則執(zhí)行其 run 方法
// 進(jìn)而創(chuàng)建超時(shí)響應(yīng)
Timeout timeout = TimerHolder.getTimer().newTimeout(new TimerTask() {
public void run(Timeout timeout) throws Exception {
InvokeFuture future = conn.removeInvokeFuture(request.getId());
// 創(chuàng)建超時(shí)響應(yīng) + 設(shè)置超時(shí)響應(yīng) + 喚醒阻塞線程
future.putResponse(commandFactory.createTimeoutResponse(conn.getRemoteAddress()));
}
}, timeoutMillis, TimeUnit.MILLISECONDS);
// 將 Timeout 設(shè)置到 InvokeFuture 中亲怠,后續(xù)可以從 InvokeFuture 中取出 Timeout,執(zhí)行取消超時(shí)任務(wù)的操作
// 超時(shí)任務(wù)的取消有兩種情況:正常返回響應(yīng) + 如下發(fā)生異常
future.addTimeout(timeout);
conn.getChannel().writeAndFlush(request);
} catch (Exception e) {
InvokeFuture f = conn.removeInvokeFuture(request.getId());
// 如果發(fā)生異常柠辞,取消超時(shí)任務(wù)
f.cancelTimeout();
f.putResponse(commandFactory.createSendFailedResponse(conn.getRemoteAddress(), e));
}
return future;
}
============================ RpcResponseProcessor ============================
public void doProcess(RemotingContext ctx, RemotingCommand cmd) {
// 獲取 Connection
Connection conn = ctx.getChannelContext().channel().attr(Connection.CONNECTION).get();
// 獲取 InvokeFuture
InvokeFuture future = conn.removeInvokeFuture(cmd.getId());
// 設(shè)置響應(yīng)团秽,喚醒阻塞線程
future.putResponse(cmd);
// 取消 timeout task
future.cancelTimeout();
// 回調(diào) callback
future.executeInvokeCallback();
}
異步方式(callback 模式)
============================ class RpcRemoting extends BaseRemoting ==================
public void invokeWithCallback(Connection conn, Object request, InvokeContext invokeContext, InvokeCallback invokeCallback, int timeoutMillis) {
// 創(chuàng)建請(qǐng)求:設(shè)置 int timeout = timeoutMillis(默認(rèn)為-1,表示永不超時(shí))
// 該 timeout 參數(shù)會(huì)用在被調(diào)用端判斷是否超時(shí)钾腺,進(jìn)而做 fail-fast 操作
RemotingCommand requestCommand = toRemotingCommand(request, conn, invokeContext, timeoutMillis);
...
// 發(fā)出請(qǐng)求
super.invokeWithCallback(conn, requestCommand, invokeCallback, timeoutMillis);
}
============================ BaseRemoting ============================
protected void invokeWithCallback(Connection conn, RemotingCommand request, InvokeCallback invokeCallback, int timeoutMillis) {
// 創(chuàng)建 InvokeFuture
InvokeFuture future = createInvokeFuture(conn, request, request.getInvokeContext(), invokeCallback);
// 添加 InvokeFuture 到 Connection
conn.addInvokeFuture(future);
try {
// 創(chuàng)建超時(shí)任務(wù)徙垫,添加到 Netty 的 hashWheel 中讥裤,在 timeoutMillis 之后如果該超時(shí)任務(wù)沒(méi)有被取消放棒,則執(zhí)行其 run 方法
// 進(jìn)而創(chuàng)建超時(shí)響應(yīng)
Timeout timeout = TimerHolder.getTimer().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
InvokeFuture future = conn.removeInvokeFuture(request.getId());
// 創(chuàng)建超時(shí)響應(yīng) + 設(shè)置超時(shí)響應(yīng)
future.putResponse(commandFactory.createTimeoutResponse(conn.getRemoteAddress()));
// 想較于 future 而言,多出這一步己英,執(zhí)行異臣涿回調(diào) callback.onException(e)
future.tryAsyncExecuteInvokeCallbackAbnormally();
}
}, timeoutMillis, TimeUnit.MILLISECONDS);
// 將 Timeout 設(shè)置到 InvokeFuture 中,后續(xù)可以從 InvokeFuture 中取出 Timeout损肛,執(zhí)行取消超時(shí)任務(wù)的操作
// 超時(shí)任務(wù)的取消有兩種情況:正常返回響應(yīng) + 如下發(fā)生異常
future.addTimeout(timeout);
conn.getChannel().writeAndFlush(request);
} catch (Exception e) {
InvokeFuture f = conn.removeInvokeFuture(request.getId());
// 如果發(fā)生異常厢破,取消超時(shí)任務(wù)
f.cancelTimeout();
f.putResponse(commandFactory.createSendFailedResponse(conn.getRemoteAddress(), e));
// 想較于 future 而言,多出這一步治拿,執(zhí)行異衬幔回調(diào) callback.onException(e)
f.tryAsyncExecuteInvokeCallbackAbnormally();
}
}
============================ RpcResponseProcessor ============================
// 這里與 future 的處理方式一樣
public void doProcess(RemotingContext ctx, RemotingCommand cmd) {
// 獲取 Connection
Connection conn = ctx.getChannelContext().channel().attr(Connection.CONNECTION).get();
// 獲取 InvokeFuture
InvokeFuture future = conn.removeInvokeFuture(cmd.getId());
// 設(shè)置響應(yīng),喚醒阻塞線程
future.putResponse(cmd);
// 取消 timeout task
future.cancelTimeout();
// 回調(diào) callback
future.executeInvokeCallback();
}
心跳請(qǐng)求:RpcHeartbeatTrigger#heartbeatTriggered(final ChannelHandlerContext ctx) 在發(fā)送了心跳請(qǐng)求之后劫谅,也是用了 TimeOut 做超時(shí)任務(wù)见坑,與 Callback 使用一致,不再分析捏检。
三荞驴、快速失敗機(jī)制(被調(diào)用端)
以上都是在分析調(diào)用端的超時(shí)邏輯彬犯,對(duì)于三種調(diào)用方式充尉,被調(diào)用端的處理都是一套邏輯。
public class RpcRequestCommand extends RequestCommand {
// 調(diào)用超時(shí)時(shí)間觉啊,調(diào)用端進(jìn)行設(shè)置能犯,默認(rèn)值為 -1鲫骗,代表永不超時(shí)
private int timeout = -1;
// 不會(huì)序列化到對(duì)端犬耻,該值會(huì)在被調(diào)用端的解碼器收到該消息時(shí),設(shè)置為收到的當(dāng)前時(shí)間
private transient long arriveTime = -1;
}
public class RpcCommandDecoder implements CommandDecoder {
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
...
RequestCommand command;
if (cmdCode == CommandCode.HEARTBEAT_VALUE) {
// 如果是心跳請(qǐng)求消息执泰,直接創(chuàng)建一個(gè) HeartbeatCommand
command = new HeartbeatCommand();
} else {
// 如果是正常請(qǐng)求香追,創(chuàng)建一個(gè) RpcRequestCommand
command = createRequestCommand(cmdCode);
}
...
out.add(command);
}
private RpcRequestCommand createRequestCommand(short cmdCode) {
RpcRequestCommand command = new RpcRequestCommand();
...
// 設(shè)置請(qǐng)求到達(dá)時(shí)間
command.setArriveTime(System.currentTimeMillis());
return command;
}
}
public class RpcRequestProcessor {
public void process(RemotingContext ctx, RpcRequestCommand cmd, ExecutorService defaultExecutor) {
// 反序列化 className
if (!deserializeRequestCommand(ctx, cmd, RpcDeserializeLevel.DESERIALIZE_CLAZZ)) {
return;
}
// 獲取 userProcessor
UserProcessor userProcessor = ctx.getUserProcessor(cmd.getRequestClass());
// 將 userProcessor.timeoutDiscard() 設(shè)置到 RemotingContext 中
// userProcessor.timeoutDiscard() 用于 fail-fast,該值默認(rèn)為 true坦胶,如果要關(guān)閉 fail-fast 功能透典,需要手動(dòng)覆蓋 UserProcessor#timeoutDiscard()
ctx.setTimeoutDiscard(userProcessor.timeoutDiscard());
... 不管是 IO 線程執(zhí)行還是業(yè)務(wù)線程池執(zhí)行
// ProcessTask.run() 調(diào)用 doProcess()
executor.execute(new ProcessTask(ctx, cmd));
}
public void doProcess(final RemotingContext ctx, RpcRequestCommand cmd) throws Exception {
long currentTimestamp = System.currentTimeMillis();
// 設(shè)置超時(shí)時(shí)間 timeout 與到達(dá)時(shí)間 arriveTime 到 RemotingContext 中,為后續(xù)計(jì)算是否超時(shí)(ctx.isRequestTimeout())做準(zhǔn)備
preProcessRemotingContext(ctx, cmd, currentTimestamp);
// 如果開(kāi)啟了 fail-fast 并且 (System.currentTimeMillis() - this.arriveTimestamp) > this.timeout 確實(shí)超時(shí)顿苇,直接返回
// 不再進(jìn)行后續(xù)的反序列化和業(yè)務(wù)邏輯處理
if (ctx.isTimeoutDiscard() && ctx.isRequestTimeout()) {
return;// then, discard this request
}
// decode request all
if (!deserializeRequestCommand(ctx, cmd, RpcDeserializeLevel.DESERIALIZE_ALL)) {
return;
}
// 派發(fā)請(qǐng)求峭咒,做真正的業(yè)務(wù)邏輯處理
dispatchToUserProcessor(ctx, cmd);
}
private void preProcessRemotingContext(RemotingContext ctx, RpcRequestCommand cmd, long currentTimestamp) {
// 將 RpcRequestCommand.getArriveTime()(該值在解碼器中進(jìn)行設(shè)置)設(shè)置到 RemotingContext 中
ctx.setArriveTimestamp(cmd.getArriveTime());
// 將 RpcRequestCommand.getTimeout() 設(shè)置到 RemotingContext 中
ctx.setTimeout(cmd.getTimeout());
...
}
// RemotingContext#isRequestTimeout()
public boolean isRequestTimeout() {
// timeout = -1 表示永不超時(shí)
// oneway 沒(méi)有超時(shí)概念
// 是否超時(shí)是按照當(dāng)前時(shí)間減去響應(yīng)達(dá)到時(shí)間,而非被調(diào)用端的當(dāng)前時(shí)間 - 調(diào)用端的請(qǐng)求發(fā)送時(shí)間(這樣計(jì)算就是使用了兩個(gè)機(jī)器的時(shí)鐘纪岁,跨機(jī)器的時(shí)鐘會(huì)有時(shí)鐘差)
if (this.timeout > 0
&& (this.rpcCommandType != RpcCommandType.REQUEST_ONEWAY)
&& (System.currentTimeMillis() - this.arriveTimestamp) > this.timeout) {
return true;
}
return false;
}
}
注意
- 是否超時(shí)是按照當(dāng)前時(shí)間減去響應(yīng)達(dá)到時(shí)間凑队,而非被調(diào)用端的當(dāng)前時(shí)間 - 調(diào)用端的請(qǐng)求發(fā)送時(shí)間(這樣計(jì)算就是使用了兩個(gè)機(jī)器的時(shí)鐘,跨機(jī)器的時(shí)鐘會(huì)有時(shí)鐘差)
- fail-fast 機(jī)制是由 UserProcessor#timeoutDiscard() 指定的幔翰,如果返回 true漩氨,則打開(kāi) fail-fast,默認(rèn)情況下 fail-fast 機(jī)制是打開(kāi)的遗增;如果想關(guān)閉該功能叫惊,手動(dòng)覆蓋 UserProcessor#timeoutDiscard() 方法即可,直接返回 false做修,之后是否超時(shí)就從
DefaultBizContext#isRequestTimeout()
進(jìn)行判斷
DefaultBizContext#isRequestTimeout() 實(shí)際上還是調(diào)用了 RemotingContext#isRequestTimeout()
private RemotingContext remotingCtx;
@Override
public boolean isRequestTimeout() {
return this.remotingCtx.isRequestTimeout();
}