从源码分析Request结束生命周期的处理流程

         

          最近在项目中遇到 由异步执行任务导致的Request中的请求入参对象被不同请求污染的多线程问题;

          在同事的研究下,发现了一把Request 在源码层面是如何进行回收处理的;因此将其中涉及到的主要源码进行展示:

 

         首先,根据源码断点我们发现Request采用了包装器模式,而最核心处理的Request是 

org.apache.catalina.connector.Request;   

         我们获取请求参数的方法源码为:

  public Map<String, String[]> getParameterMap() {

        //当 parameterMap 被锁住的时候,直接返回对象(整个Request生命周期都使用的这个)
        if (parameterMap.isLocked()) {
            return parameterMap;
        }

        //当未锁住的时候进行初始化和赋值,并在赋值结束后将其锁住
        //在parameterMap  被释放锁之后第一次调用执行(可能被异步调用执行,可能被下一个请求对象执行)
        Enumeration<String> enumeration = getParameterNames();
        while (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement();
            String[] values = getParameterValues(name);
            parameterMap.put(name, values);
        }

        parameterMap.setLocked(true);

        return parameterMap;

    }

         从我们获取请求Request 中的请求入参可知:  

             当一次请求结束前,我们将Request交给一个异步执行的线程使用,会出现两种情况

             1、在下一次请求前执行getParameterMap()   方法  

             2、在下一次请求后执行getParameterMap()  方法

            而这两种情况都会到来问题:

            首先,在下一次请求前执行getParameterMap()   方法  这个时候我们的请求Enumeration<String> enumeration 对象已经被清理,没有任何入参,并且parameterMap对象被锁住,导致下一个请求无法进行参数设置,就会出现前端请求参数无法设置进去的现象;

            第二种情况,在下一次请求后执行getParameterMap()  方法,这个时候下一个请求的入参会正常执行,但是因为下一次请求的入参被设置进来,并且我们是引用指向,parameterMap对象并不会被真正的被重新创建,他只是通过Lock控制了数据的安全,因此我们一步请求的线程依然指向下一个接口请求的parameterMap,并且其中的参数值为下一个请求接口的入参值。

 

为了更便于了解真相,下面我们把Request的重置方法源码贴出来:


    public void recycle() {

        internalDispatcherType = null;
        requestDispatcherPath = null;

        authType = null;
        inputBuffer.recycle();
        usingInputStream = false;
        usingReader = false;
        userPrincipal = null;
        subject = null;
        parametersParsed = false;
        if (parts != null) {
            for (Part part: parts) {
                try {
                    part.delete();
                } catch (IOException ignored) {
                    // ApplicationPart.delete() never throws an IOEx
                }
            }
            parts = null;
        }
        partsParseException = null;
        locales.clear();
        localesParsed = false;
        secure = false;
        remoteAddr = null;
        remoteHost = null;
        remotePort = -1;
        localPort = -1;
        localAddr = null;
        localName = null;

        attributes.clear();
        sslAttributesParsed = false;
        notes.clear();

        recycleSessionInfo();
        recycleCookieInfo(false);

        //入参内容保存Map, 
        if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) {
            //处理方式1: new一个,引用地址变更,与老参数引用地址不形成任何影响
            parameterMap = new ParameterMap<>();
        } else {
            //处理方式2:去除锁,清除参数;但是对象的引用不变;
            parameterMap.setLocked(false);
            parameterMap.clear();
        }

        mappingData.recycle();
        applicationMapping.recycle();

        applicationRequest = null;
        if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) {
            if (facade != null) {
                facade.clear();
                facade = null;
            }
            if (inputStream != null) {
                inputStream.clear();
                inputStream = null;
            }
            if (reader != null) {
                reader.clear();
                reader = null;
            }
        }

        asyncSupported = null;
        if (asyncContext!=null) {
            asyncContext.recycle();
        }
        asyncContext = null;
    }

 

其中对parameterMap 的操作代码如下

         //入参内容保存Map, 
        if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) {
            //处理方式1: new一个,引用地址变更,与老参数引用地址不形成任何影响
            parameterMap = new ParameterMap<>();
        } else {
            //处理方式2:去除锁,清除参数;但是对象的引用不变;
            parameterMap.setLocked(false);
            parameterMap.clear();
        }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值