异步线程拿请求头

异步线程获取request,获取请求头数据和参数被清空问题处理

dawnStart

已于 2023-11-21 17:54:12 修改

阅读量308
收藏 2

点赞数
文章标签: tomcat spring boot java
版权
其实在tomcat中,request和response是会被复用的,tomcat会维护一个请求池,每次都会从中拿到request设置参数,然后开始一次请求,然后请求结束响应后,会将request和response重置,然后将其放到请求池中,等待后续的复用.

有时我们在异步线程外面使用RequestContextHolder.currentRequestAttributes();
获取到了当前的请求对象,然后再放到异步线程中
RequestContextHolder.setRequestAttributes(requestAttributes);
然后在异步线程中获取请求HttpServletRequest,然后拿请求头,可能获取的是空的,这是因为在异步线程执行完之前,外面的请求线程已经结束了,然后request就被tomcat重置放入池中了,所以导致request请求头,请求参数被重置了,解决这个问题有两种方案
方案一: 将当前请求标记为异步请求,这样tomcat在回收request的时候,就会等待,等待异步完成

AsyncContext asyncContext = request.startAsync();
// todo 这里执行你想要操作的事情,完成后记得调用complete
asyncContext.complete();

// 上面的方案有缺陷,tomcat会一直等待request异步执行完成调用complete方法,
//导致前端请求会一直转圈圈,所以可以用下面的方案,让tomcat再起一个线程执行你的异步方法
// 这里也不太好,用的是tomcat的线程池,如果想用自己定义的线程池,还是不太行
// 所以可以用下面的方案二
AsyncContext asyncContext = request.startAsync();
asyncContext.start(()->{

     // todo 做你要做的事
     asyncContext.complete();
 });

方案二:
拷贝原线程的request,然后再放入异步线程
工具类的代码如下

import org.apache.catalina.connector.Request;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
public class RequestCopy {

private RequestCopy() {
    throw new IllegalStateException("Utility class");
}

/**
 * 拷贝当前线程中的request
 * 注意:仅拷贝了需要的header,异步调用feign接口时所需要的
 * @return
 */
public static RequestAttributesCopy copyCurrentThreadRequestAttributes(){
    ServletRequestAttributes requestAttributes = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes());
    HttpServletRequest request = requestAttributes.getRequest();
    return copyToRequestAttributes(request);
}

/**
 * 拷贝request中的header
 * 注意:仅拷贝了需要的header,异步调用feign接口时所需要的
 * @param request
 * @return
 */
public static RequestAttributesCopy copyToRequestAttributes(HttpServletRequest request) {
    return new RequestAttributesCopy(copyToRequest(request));
}

/**
 * 拷贝request
 * 注意:仅拷贝了需要的header,异步调用feign接口时所需要的
 * @param request
 * @return
 */
public static HttpServletRequestCopy copyToRequest(HttpServletRequest request) {
    Map<String, String> headerMap = new HashMap();
    Enumeration<String> headers = request.getHeaderNames();
    if (headers != null) {
        while (headers.hasMoreElements()) {
            String header = (String) headers.nextElement();
                headerMap.put(header, request.getHeader(header));
            }
        }
    }
    return new HttpServletRequestCopy(headerMap);
}

public static class RequestAttributesCopy extends ServletRequestAttributes {

    public RequestAttributesCopy(HttpServletRequestCopy request) {
        super(request);
    }
}

public static class HttpServletRequestCopy extends Request {

// 自己定义的请求头保存,看自己的需求来,如果还想获取参数,那就加个参数保存就好
private final Map<String, String> header;

    public HttpServletRequestCopy(Map<String, String> header) {
        super(null);
        if (header == null) {
            header = Collections.emptyMap();
        }
        this.header = header;
    }

// 这里重写你需要获取的参数,我这边就只用获取请求头,所以就重写了请求头的获取方法
@Override
public String getHeader(String name) {
return header.get(name);
}

    @Override
    public Enumeration<String> getHeaderNames() {
        return Collections.enumeration(header.keySet());
    }
}

}

使用如下

// 异步导出,这里是传递request到异步线程中,传递token需要(需要拷贝一份,不然请求结束了,原来的request会被清空)
RequestCopy.RequestAttributesCopy requestAttributesCopy = RequestCopy.copyCurrentThreadRequestAttributes();
new Thread(()->{
RequestContextHolder.setRequestAttributes(requestAttributesCopy);
// todo 做你想做的事
}).start();

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值