項目中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的有點真的是毋庸置疑,減輕了後端服務的壓力,後端服務性能飆升。服務器