项目中E端有一个订单导出的功能能(导出销售订单或者销售退单,导出列颇多,且必须知足实时数据)。咱们使用POI导出数据,而且后端加了熔断措施,导出限流,大促期间导出开关控制。相对来讲有了这些机制线上应用不会由于导出操做流量过大内存爆掉,也保证了应用安全稳定的运行,可是最近监控发现导出操做性能急剧降低(数据量已经超过3百万),先看看监控。前端
再来看看使用POI导出本地jvisualvm的内存动态变化图。redis
这里咱们主要分析第二个点,对于第一点你们都清楚如何解决问题。首先应该思考为何使用POI导出的时候内存飙升的那么快呢?后端
结合Thread Dump能够看出,导出的时候内存增加过快,在数据量大和请求量过大的状况下,内存极速增加,然而这个过程当中大量对象存活在年轻代,在年轻代没法被回收直接进入老年代。整体来讲POI使用XMLBean处理Dom写Excel文件,内存占用过大,耗费资源;而且导出速度满,占用内存资源时间过长,致使一系列恶性循环。安全
既然POI导出有这些不足之处,如何解决这样的问题呢?思路很简单,再也不使用POI导出。下降服务端资源占用。后端服务能够只查询JSON数据,导出的工做交给客户端,这样彻底屏蔽掉了使用POI导出的问题,能够想象,这样作就是一个简单的restful列表查询接口。bash
String uuid = UUID.randomUUID().toString();
RedisAtomicLong counter = new RedisAtomicLong(ORDER_EXPORT_UUID + uuid, cacheRedisTemplate.getConnectionFactory());
counter.set(0);
counter.expire(60, TimeUnit.SECONDS);
复制代码
public class ExportDTO<T> {
private Boolean HasNext = true;
private T data;
}
复制代码
public boolean existKey(String uuid) {
return cacheRedisTemplate.getExpire(ORDER_EXPORT_UUID + uuid) > 0;
}
public void deleteUUID(String uuid) {
cacheRedisTemplate.delete(ORDER_EXPORT_UUID + uuid);
}
public long incrementAndGetUUIDValue(String uuid) {
RedisAtomicLong counter = new RedisAtomicLong(ORDER_EXPORT_UUID + uuid, cacheRedisTemplate.getConnectionFactory());
return counter.incrementAndGet();
}
public List<SalesOrderExportDTO> getSaleOrderData(String uuid, ExportOrderQueryDTO orderQuery, boolean showPhoneNumFlag) {
if (!existKey(uuid)) {
return null;
}
long exportCount = incrementAndGetUUIDValue(uuid);
//导出次数限制(这里一次查询1000条,最多查询30次,导出最大值为3万)
if (exportCount > EXPORT_MAX_PAGE) {
deleteUUID(uuid);
return null;
}
//验证最大导出值
if (exportCount == 1) {
int totalCount = exportMapper.countSaleOrders(orderQuery);
Preconditions.checkArgument(totalCount < EXPORT_ONCE * EXPORT_MAX_PAGE, "最多导出%s条数据", EXPORT_ONCE * EXPORT_MAX_PAGE);
}
orderQuery.setOffset((exportCount - 1) * EXPORT_ONCE);
orderQuery.setLimit(EXPORT_ONCE);
List<Long> ids = exportMapper.listSaleOrderIds(orderQuery);
if (CollectionUtils.isEmpty(ids)) {
deleteUUID(uuid);
return null;
}
List<SalesOrderExportDTO> orderExportDTOS = exportMapper.listSaleOrders(ids);
}
复制代码
通过这么多天的线上应用内存观察,前端导出Excel的有点真的是毋庸置疑,减轻了后端服务的压力,后端服务性能飙升。服务器