InputStream流本身是單向的甫煞,無法重復(fù)讀取,為了實現(xiàn)流的可重復(fù)讀冠绢,需要對流進(jìn)行裝飾抚吠。但是對流的裝飾,卻引發(fā)了系統(tǒng)OOM
裝飾方案:
public class AdBizTraceLogFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
ContentCachingRequestWrapper reqWrapper = getRequestWrapper(request);
response = getResponseWrapper(response);
try {
filterChain.doFilter(reqWrapper, response);
} finally {
// 業(yè)務(wù)層讀取一次body后, 后續(xù)都通過getContentAsByteArray讀取
String reqBody = new String(reqWrapper.getContentAsByteArray(), "UTF-8");
ContentCachingResponseWrapper respWrapper = (ContentCachingResponseWrapper) response;
String respBody = new String(respWrapper.getContentAsByteArray(), "UTF-8");
respWrapper.copyBodyToResponse();
}
}
private ContentCachingRequestWrapper getRequestWrapper(HttpServletRequest request) {
ContentCachingRequestWrapper requestWrapper;
//此處有bug弟胀,會導(dǎo)致大數(shù)據(jù)的請求或者響應(yīng)對象的占用雙份內(nèi)存?Α!孵户!
if (request instanceof ContentCachingRequestWrapper) {
requestWrapper = (ContentCachingRequestWrapper) request;
} else {
requestWrapper = new ContentCachingRequestWrapper(request);
}
return requestWrapper;
}
private ContentCachingResponseWrapper getResponseWrapper(HttpServletResponse response) {
ContentCachingResponseWrapper responseWrapper;
if (response instanceof ContentCachingResponseWrapper) {
responseWrapper = (ContentCachingResponseWrapper) response;
} else {
responseWrapper = new ContentCachingResponseWrapper(response);
}
return responseWrapper;
}
}
將流裝飾成可重復(fù)讀的流萧朝。但是如果系統(tǒng)存在大批量文件上傳,需要做一下控制夏哭,判斷contentType检柬。
if (request.getContentLength() > MAX_CONTENT_LENGTH) {
PerfUtils.perf(LOG_NS, "ignorePath", "max length").logstash();
return true;
}
String contentType = request.getHeader("Content-Type");
if (contentType == null || !contentType.toLowerCase().contains("application/json")) {
PerfUtils.perf(LOG_NS, "ignorePath", "content type").logstash();
return true;
}
在新版的Spring中ContentCachingRequestWrapper中使用FastByteArrayOutputStream來取代ContentCachingRequestWrapper,不需要初始化ContentCachingRequestWrapper的時候就申請大塊內(nèi)存竖配,lazy化的何址。
https://github.com/spring-projects/spring-framework/commit/f83c6094362b7e16fe08a4307cbcb0015e203d23