Skip to main content

开发者调用OpenAPI超时

本文介绍了钉钉服务端OpenAPI超时的判定条件、解决办法和最佳实践。

一般情况下,开发者调用钉钉开放平台的OpenAPI会出现调用超时的情况,目前钉钉开放平台的OpenAPI调用成功率能保证在99.95%,但是仍有一些情况下会出现异常,然后调用API超时也是一种异常情况,出现该异常是正常现象,但是针对这一现象目前有比较多的开发者进行咨询解决方案,现钉钉提供整一套的解决方案和最佳实践案例,供各位开发者进行参考。

现象一:调用钉钉开放平台API小面积出现超时情况下,开发者又想拿到正确的响应结果,建议开发者可以按照本文下列所述的解决方案进行排查和解决。

现象二:调用钉钉开放平台API大面积出现超时情况下,这种情况下可能钉钉的服务端有负载过高的情况,可以联系技术支持提交反馈寻求帮助。

触发条件

在开发者调用钉钉开放平台的接口时,如果在3s内钉钉服务端未做出响应就会响应超时,此时调用者会获取到对应的超时错误请求。

判定条件

  • 旧版OpenAPI返回以下错误码表示调用超时:
错误码错误说明备注【子错误码】
15调用超时isp.top-remote-connection-timeout
88鉴权异常如果子错误码为空,则鉴权服务超时。
  • 新版OpenAPI返回以下错误码表示调用超时:
错误码【http状态码】错误说明备注
504Gateway Timeout调用网关超时

解决办法

调用钉钉的开放接口的api出现上述所示的错误码之后,表示当前请求响应超时,此时开发者可以对接口调用做超时重试处理机制,正常情况下立即重试能获取成功响应的结果,但对于一些暂时性的错误,如网络抖动等,可能立即重试还是会失败,通常等待一小会儿再重试的话成功率会较高,并且也可能打散上游重试的时间,较少因为同时都重试而导致的下游瞬间流量高峰。决定等待多久之后再重试的方法叫做退避策略,我们提供了常见的退避策略方案,如:【具体见最佳实践】

  • 立即重试:调用报错立即发起重试调用。
  • 线性退避:每次等待固定时间后重试。
  • 随机退避:在一定范围内随机等待一段时间后重试。
  • 指数退避:连续重试时,每次等待时间都是前一次的倍数。

最佳实践

如果接口超时的几率比较大,建议开发者按照指数退避策略进行接入,如果出现的几率较小,可以立即重试或采用线性退避策略,以下是一些请求伪代码示例,仅供参考:

  • 立即重试
class Retry {
public TaobaoResponse retryCall() {
TaobaoResponse response = client.execute(req,"accessKey","accessSecret","suiteTicket");
if (("15".equals(response.getErrorCode()) && "isp.top-remote-connection-timeout".equals(response.getSubCode()))
|| ("88".equals(response.getErrorCode()) && StringUtils.isEmpty(response.getSubCode()))) {
// 判断为超时请求,立即发起重试
response = client.execute(req,"accessKey","accessSecret","suiteTicket");
}
return response;
}
}
  • 线性退避
class Retry {
public TaobaoResponse linearRetry() {
int maxRetryTimes = 3;
TaobaoResponse response;
for (int retry = 1; retry <= maxRetryTimes; retry++) {
response = client.execute(req,"accessKey","accessSecret","suiteTicket");
if (("15".equals(response.getErrorCode()) && "isp.top-remote-connection-timeout".equals(response.getSubCode()))
|| ("88".equals(response.getErrorCode()) && StringUtils.isEmpty(response.getSubCode()))) {
Thread.sleep(1000); // 固定线性等待时间发起重试
} else {
// 打印请求结果,此处有可能成功或失败,但不是超时异常,所以不继续下次重试。
break;
}
}
return response;
}
}
  • 随机退避
class Retry {
public TaobaoResponse randomRetry() {
Retryer<TaobaoResponse> retry = RetryerBuilder.<TaobaoResponse>newBuilder() // 此处示例是借助开源guava-retrying工具
.retryIfResult( response -> ("15".equals(response.getErrorCode()) && "isp.top-remote-connection-timeout".equals(response.getSubCode()))
|| ("88".equals(response.getErrorCode()) && isEmpty(response.getSubCode())))
.withWaitStrategy(WaitStrategies.randomWait(3, TimeUnit.SECONDS)) // 随机等待,最大时长3s
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试最大次数
.build();
try {
return retry.call(() -> client.execute(req,"accessKey","accessSecret","suiteTicket"));
} catch (Exception e){
e.printStackTrace();
}
}
}
  • 指数退避
class Retry {
public TaobaoResponse indexRetry() {
Retryer<TaobaoResponse> retry = RetryerBuilder.<TaobaoResponse>newBuilder()
.retryIfResult( response -> ("15".equals(response.getErrorCode()) && "isp.top-remote-connection-timeout".equals(response.getSubCode()))
|| ("88".equals(response.getErrorCode()) && isEmpty(response.getSubCode())))
.withWaitStrategy(WaitStrategies.exponentialWait(500, 10, TimeUnit.SECONDS)) // 等待策略:指数等待
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试最大次数
.build();
try {
return retry.call(() -> client.execute(req,"accessKey","accessSecret","suiteTicket"));
} catch (Exception e){
e.printStackTrace();
}
}
}