很多公司业务都需要进行第三方接口的对接工作,特别是那种大部分数据都来自第三方的项目。比如亚马逊商家服务的saas系统,基本上所有的数据都来自亚马逊平台。
背景
像这种需要定期获取亚马逊接口数据,然后存储到本地数据库中的项目,一般就会涉及到数据转换过程。这边我将会给大家介绍一个实际项目案例,平台从亚马逊获取数据,进行解析的过程中,因为数据过大,导致内存溢出的场景。
接口说明
亚马逊平台返回的是json格式数据,然后通过jackson进行json解析,将最后的解析结果保存到我们自己的数据库中。但是json反序列也是需要技巧的,否则会因为使用不当导致内存溢出。
json解析
一开始我们通过jackson工具类,将json流解析为JsonNode,如下所示:
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree((GZIPInputStream) reportMap.get(“data”));
但是当请求量过大的时候,内存中就会出现大量的LinkHashMap、Entry、HashMap、Node之类的集合对象,时间一长就OOM内存溢出了。
解析改造
集合对象如果没有管理好,会很难被GC回收,所以一开始我们想到的办法是将JsonNode设置为null,后面发现这样做没起到任何作用,所以只能进行彻底的改造,直接用jackson解析成我们需要的对象,如下所示:
private List parseJsonIO(GZIPInputStream gzipInputStream) {
ObjectMapper mapper = new ObjectMapper();
List advertisementDtoList =new ArrayList<>();
try {
advertisementDtoList = mapper.readValue(gz
ipInputStream, new TypeReference<List>() {
});
} catch (IOException e) {
logger.error(“parseJsonIO转化异常,错误信息为:{}”, ExceptionUtil.formatException(e));
}
return advertisementDtoList;
}
修改完之后可以很明显的看到,LinkHashMap、Entry、HashMap、Node之类的对象数量大量减少,内存的占用率明显降低,大大的减少了内存OOM的风险。
深入改造
写到这一步是不是就优化完毕了呢?不!!!一开始的时候我是将所有的字段全部设置为String,然后通过Long.value()和Integer.value()转换为数据库需要的字段类型,如下所示:
campaignSearchTermReport.setImpressions(Integer.valueOf(impressions));
campaignSearchTermReport.setTotalspend(Long.valueOf(cost));
campaignSearchTermReport.setSales(BigDecimal.valueOf(sales));
所以为了避免这种情况发生,我们需要将转换的对象字段提前设置好,数据库需要什么类型,我们就设置为同样的类型,这样的好处就是我们不需要进行转型,直接就可以set进去,如下所示:
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ReportAdvertisementDto {
private double cost;
private String attributedSales1d;
private int impressions;
private int clicks;
private long campaignId;
private String campaignName;
private String adGroupName;
private long adId;
private long adGroupId;
private String sku;
private String asin;
private long keywordId;
private String matchType;
private String query;
private String keywordText;
private int attributedConversions7d;
private double attributedSales7d;
private int attributedUnitsOrdered7d;
private int attributedConversions7dSameSKU;
private double attributedSales7dSameSKU;
}
总结
在数据量不大的情况下,代码就算效率不高,也不会出现什么问题,但是当数据量达到一定级别,代码问题就会被凸显出来。比如我们平时用Map来存储临时数据,但是map集合的大小要比对象更加占用内存,如果服务器硬件不高,很容易就发生内存溢出。
所以我们在处理接口数据的时候,一定要本着简单、适用。尽量不要将json数据解析为map等集合,对象字段尽量设置为和入库的的表字段类型一致,减少转型的发生。禁止出现大量对象和对象之间数据流转,尽量做到一个解析后的对象直接入库,不需要进行任何转型操作。
想学习分布式、微服务、JVM、多线程、架构、java、python的童鞋,千万不要扫码,否则后果自负~
林老师带你学编程:https://wolzq.com
最后
小编这些年深知大多数初中级工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Java全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你需要这些资料,⬅专栏获取
S0u5sAPe-1719684124748)]
[外链图片转存中…(img-VtQm0Uug-1719684124748)]
[外链图片转存中…(img-ESIy3uK6-1719684124748)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你需要这些资料,⬅专栏获取