这篇文章整理两个可以直接复用的 Java 片段:获取国际电话区号,以及为不稳定的外部调用增加指数退避重试。
获取国家和地区的国际区号
Google libphonenumber 内置了电话号码元数据,可以从 ISO 3166-1 地区代码映射到国际区号。
Maven 依赖可以统一由项目的版本管理控制:
<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>libphonenumber</artifactId>
<version>${libphonenumber.version}</version>
</dependency>
保留地区与区号的对应关系
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
Map<String, Integer> regionToCallingCode = phoneUtil.getSupportedRegions()
.stream()
.sorted()
.collect(Collectors.toMap(
Function.identity(),
phoneUtil::getCountryCodeForRegion,
(left, right) -> left,
LinkedHashMap::new
));
这里得到的是“地区代码 → 国际电话区号”,例如 CN → 86。它不是城市固定电话的国内区号。
只获取不重复的区号
多个地区可能共享同一个国际区号,因此只需要数字列表时应去重:
List<Integer> callingCodes = phoneUtil.getSupportedRegions()
.stream()
.map(phoneUtil::getCountryCodeForRegion)
.distinct()
.sorted()
.collect(Collectors.toList());
电话号码规则会变化,应用应定期升级 libphonenumber 元数据,而不是把结果永久固化。
使用指数退避执行重试
指数退避会逐步拉长两次调用之间的等待时间,适用于短暂网络故障、服务限流或临时不可用。它不应掩盖参数错误、权限错误等不可恢复异常。
引入 guava-retrying
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>2.0.0</version>
</dependency>
配置 Retryer
private final Retryer<Void> retryer = RetryerBuilder.<Void>newBuilder()
.retryIfException()
.withStopStrategy(StopStrategies.stopAfterAttempt(5))
.withWaitStrategy(
WaitStrategies.exponentialWait(1000, 32, TimeUnit.SECONDS)
)
.build();
这段配置最多执行 5 次(包含首次调用),等待时间按指数增长并封顶在 32 秒。具体等待序列由库内部的尝试次数计算决定,不要把它理解为固定定时任务。
执行业务调用
try {
retryer.call(() -> {
remoteClient.send();
return null;
});
} catch (RetryException e) {
// 已达到停止条件
throw new IllegalStateException("调用重试后仍然失败", e);
} catch (ExecutionException e) {
// Callable 执行过程中出现无法继续处理的异常
throw new IllegalStateException("调用执行失败", e.getCause());
}
生产使用建议
- 不要无差别重试所有异常,应尽量限定为超时、连接重置、HTTP 429/503 等可恢复错误。
- 对非幂等写操作增加幂等键,避免重试造成重复扣款或重复创建订单。
- 指数退避最好叠加随机抖动,避免大量实例同时重试形成“惊群”。
- 记录尝试次数、最终失败原因和总耗时,便于监控重试是否正在放大故障。