背景
用户上传文件并显示解析后内容。
后端依赖解析服务实现解析功能,由于解析服务处理后的数据存储在redis,需要后端服务主动去redis中查询数据。(解析服务不提供同步接口)
解决方案
- 前端上传后轮询查询解析结果
- 服务端轮询查询解析结果,同步返回(本次实践的方案)
思路
为确保前端能获取到文件解析结果,后端服务需要增加重试机制调用解析服务接口获取文件解析结果,直到成功获取到解析数据或者达到设定重试次数后同步返回处理结果
代码
MethodPoller
[@Slf4j](https://my.oschina.net/slf4j)
public class MethodPoller<T> {
int maxTimes;
AtomicInteger times = new AtomicInteger(0);
int pollIntervalMillis;
private Supplier<T> pollMethod = null;
private Predicate<T> pollResultPredicate = null;
public MethodPoller<T> poll(int maxTimes, int pollIntervalMillis) {
this.maxTimes = maxTimes;
this.pollIntervalMillis = pollIntervalMillis;
return this;
}
public MethodPoller<T> method(Supplier<T> supplier) {
pollMethod = supplier;
return this;
}
public MethodPoller<T> until(Predicate<T> predicate) {
pollResultPredicate = predicate;
return this;
}
public T execute() {
T result = null;
boolean pollSucceeded = false;
while (!pollSucceeded) {
if (times.get() > maxTimes) {
log.info("execute fail");
return result;
}
times.incrementAndGet();
result = pollMethod.get();
pollSucceeded = pollResultPredicate.test(result);
if (!pollSucceeded) {
try {
Thread.sleep(pollIntervalMillis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalArgumentException(e);
}
}
}
log.info("execute success");
return result;
}
}
FileAnalyzeRedisClient
[@Component](https://my.oschina.net/u/3907912)
public class FileAnalyzeRedisClient {
public FileAnalyzeResultDTO getResult(String requestId) {
String result = (String) redisTemplate.opsForValue().get(requestId);
// 由于解析服务异步处理,可能导致Redis中结果为空(未识别完成)
if (ObjectUtils.isEmpty(result)) throw new ExceptionManager(10003);
return JsonUtils.jsonToBean(result, FileAnalyzeResultDTO.class);
}
}
FileAnalyzeService
public class FileAnalyzeService {
public FileAnalyzeResultDTO analyzeFile(MultipartFile multipartFile) {
// 省略调用解析服务接口
String hash = DigestUtils.md5Hex(multipartFile.getBytes());
// 解析结果
MethodPoller<FileAnalyzeResultDTO> methodPoller = new MethodPoller<>();
// 重试4次,每次间隔2000ms
FileAnalyzeResultDTO fileAnalyzeResultDTO = methodPoller.poll(4, 2000).method(() -> idCardRedisClient.getResult(hash)).until(Objects::nonNull).execute();
return fileAnalyzeResultDTO;
}
}
🌀