需求:我们需要实现一个服务器的地址下载日志文件的功能。需求就是这么简单。 在我拿到这些需求时,遇到以下几个问题。
- 如果是多个服务器部署,怎么下载。
- 基于问题1,nginx只能部署在一个服务器上,怎么进行服务器请求分发,也就是说怎么分别请求两个服务器。
针对上述两个问题: 我决定使用两个服务完成上述的需求,并且可以解决上面的问题。
具体方案
- 使用两个服务,即服务a进行请求分发,服务b完成文件下载功能并返回文件流给服务a
- 服务a调用服务b使用RestTemplate来完成
实现方法
服务b的方法
服务b的功能:实现下载功能并返回文件流,此处只展示返回体(默认会完成文件下载功能)
@PostMapping("downloadLogZip")
@ApiOperation("下载日志文件")
public ResponseEntity<InputStreamResource> downloadLogZip(HttpServletResponse response, @RequestBody DownloadLogFileRequest request) {
return logService.downloadLogZip(response, request);
}
上述的代码,其实有一个问题,是:文件下载我们都是使用的 OutputStream,但是我这块返回的是 InputStreamResource, 我是在service里面将OutputStream转成了InputStreamResource返回的
服务a的方法
服务a的功能:实现分发请求到某个服务器上的服务b,并暴露接口给前端
// 因为header中有token的信息,所以需要封装head和请求体
HttpHeaders headers = httpRequest.getHeaders();
HttpEntity<ListLogFileRequest> entity = new HttpEntity(request, headers);
// 请求下载文件接口
ResponseEntity<Resource> response = restTemplate.exchange(request.getIp() + "base/log/downloadLogZip", HttpMethod.POST, entity, Resource.class);
// 将文件流返回
InputStream inputStream = null;
HttpStatus statusCode = response.getStatusCode();
if (statusCode.is2xxSuccessful()) {
try {
inputStream = response.getBody().getInputStream();
} catch (IOException e) {
log.error("获取返回流错误:{}", response);
}
} else {
log.error("下载日志文件失败:{}", response);
}
return ResponseEntity.ok()
.header("Content-Disposition", "attachment;filename=" + filename)
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(inputStream));
上述代码,关键的是这个地方
图中的 request.getIp() 代表服务器的ip地址,这样如果服务b在两个服务器上都部署,那么通过ip的变化就可以请求到对应的服务器的服务b了。
返回体
return ResponseEntity.ok()
.header("Content-Disposition", "attachment;filename=" + filename)
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(inputStream));
通过上述代码,即可返回文件流,完成下载功能