最近有个功能,Android通过okhttp上传实体类,实体类包含一个大字段,上传的字符串长度达到300k,偶现接口超时的情况,大概100次有5次,看日志发现数据并没有到达接口,可能在网络传输中就超时了
直接接收对象:
@ApiOperation(value = "插入主动测量记录")
@PostMapping("/insertMeasureRecord")
public Result insertMeasureRecord(@RequestBody LomeRingMeasureRecords lomeRingMeasureRecords ){
return iRingAppService.insertMeasureRecord(lomeRingMeasureRecords);
}
刚开始是这样接收的,后来优化成流式解析
流式解析:
@Override
public Result insertMeasureRecordRequest(HttpServletRequest request) {
// 1. 使用流式读取避免内存爆炸
try (InputStream is = request.getInputStream();
Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
// 2. 带超时的JSON解析
CompletableFuture<LomeRingMeasureRecords> future = CompletableFuture.supplyAsync(() -> {
return JSON.parseObject(reader, LomeRingMeasureRecords.class);
});
LomeRingMeasureRecords records = future.get(15, TimeUnit.SECONDS); // 15秒超时
// 3. 继续原有处理逻辑
return Result.ok(lomeRingMeasureRecordsService.insertMeasureRecords(records));
} catch (TimeoutException e) {
return Result.fail(ResultsCode.REQUEST_FAILED);
} catch (Exception e) {
return Result.fail(ResultsCode.REQUEST_FAILED);
}
}
即使修改成这样,还是偶现超时,所以对大字段进行了压缩,然后服务端进行解压
Android部分
public static Observable<JsonResult> insertMeasureRecord(String mac,String value,String testType,String filePath,String fileName,String label,String waveForm) {
Log.i("waveForm size",waveForm.length()+"");
String compressString="";
try {
compressString = DataCovertUtils.compressString(waveForm);
} catch (IOException e) {
e.printStackTrace();
}
Log.i("compressString size",compressString.length()+"");
Map<String, Object> map = new HashMap<>();
map.put("mac", mac);
map.put("value", value);
map.put("testType", testType);
map.put("filePath", filePath);
map.put("fileName", fileName);
map.put("label", label);
map.put("compressedWaveForm", compressString);
return ServerAPIClient.getApi().insertMeasureRecord(map).subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidScheduler.mainThread());
}
// GZIP压缩字符串
public static String compressString(String str) throws IOException {
if (str == null || str.isEmpty()) {
return str;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(out)) {
gzip.write(str.getBytes(StandardCharsets.UTF_8));
}
return Base64.encodeToString(out.toByteArray(), Base64.NO_WRAP);
}
然后springboot解压
@Override
public Result insertMeasureRecordRequest(HttpServletRequest request) {
try (InputStream is = request.getInputStream();
Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
LomeRingMeasureRecords records = JSON.parseObject(reader, LomeRingMeasureRecords.class);
// 解压处理
if (records.getCompressedWaveForm() != null ) {
try {
String decompressed = decompressString(records.getCompressedWaveForm());
records.setWaveForm(decompressed);
} catch (IOException e) {
// 解压失败记录日志,保持原数据
log.error("解压waveForm失败", e);
return Result.fail(ResultsCode.REQUEST_FAILED);
}
}
return Result.ok(lomeRingMeasureRecordsService.insertMeasureRecords(records));
} catch (Exception e) {
log.error("处理测量记录请求失败", e);
return Result.fail(ResultsCode.REQUEST_FAILED);
}
}
// GZIP解压字符串
private String decompressString(String compressedStr) throws IOException {
if (compressedStr == null || compressedStr.isEmpty()) {
return compressedStr;
}
byte[] compressed = Base64.getDecoder().decode(compressedStr);
ByteArrayInputStream bis = new ByteArrayInputStream(compressed);
GZIPInputStream gis = new GZIPInputStream(bis);
// 使用ByteArrayOutputStream直接读取所有字节
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = gis.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
// 转换为字符串,保留原始换行符
String result = baos.toString(StandardCharsets.UTF_8.name());
gis.close();
bis.close();
baos.close();
return result;
}
这样原字段大概是300k,然后压缩完是55k,之前接口返回是3s,修改完是2.5s,因为涉及到python计算,没办法再提高了
按理说,字段没超过1M,不应该出现超时的情况,根本没有到达接口,可能还是网络问题