美文网首页
ContentCachingRequestWrapper实现Re

ContentCachingRequestWrapper实现Re

作者: 小胖学编程 | 来源:发表于2024-01-22 17:30 被阅读0次

    InputStream流本身是单向的,无法重复读取,为了实现流的可重复读,需要对流进行装饰。但是对流的装饰,却引发了系统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 {
                // 业务层读取一次body后, 后续都通过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,会导致大数据的请求或者响应对象的占用双份内存!!!
            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;
        }
    
    }
    

    将流装饰成可重复读的流。但是如果系统存在大批量文件上传,需要做一下控制,判断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的时候就申请大块内存,lazy化的。

    https://github.com/spring-projects/spring-framework/commit/f83c6094362b7e16fe08a4307cbcb0015e203d23

    相关文章

      网友评论

          本文标题:ContentCachingRequestWrapper实现Re

          本文链接:https://www.haomeiwen.com/subject/jbnyodtx.html