在非web请求中使用Feign完成微服务调用(定时任务,过期策略等)

某天某个地方,正在愉快的撸代码,突然异常邮件报警,赶快上服务器看日志,发现是定时任务在疯狂报错,错误如下:

No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
上古狗翻译一下,大致意思是在web请求之外使用了fegin调用微服务,急忙找源码瞅了瞅,找到了报错的地方
在非web请求中使用Feign完成微服务调用(定时任务,过期策略等)_第1张图片
可以看到只要RequestAttributes为null就会抛异常,问题找到了,顺着源码分析一下,RequestAttributes为null大概有两种原因:
1.请求是web请求,但在代码执行过程中执行了异步方法,在请求结束后RequestAttributes被销毁,具体解决方法看我的另一个博客,异步操作导致异步线程获取不到主线程的request信息
2.就是我们在一个非web方法里边调用了微服务,RequestAttributes为null也会报错,知道原因我们尝试解决:
既然RequestAttributes为null,他又是一个接口,那我们可以实现以下这个接口。我们把实现类起名为NonWebRequestAttributes,下边是代码

public class NonWebRequestAttributes implements RequestAttributes {

    public NonWebRequestAttributes(){

    }
    @Override
    public Object getAttribute(String name, int scope){return null;}

    @Override
    public void setAttribute(String name, Object value, int scope) {

    }
    @Override
    public void removeAttribute(String name, int scope){}
    @Override
    public String[] getAttributeNames(int scope){return new String[0];}

    @Override
    public void registerDestructionCallback(String name, Runnable callback, int scope) {

    }

    @Override
    public Object resolveReference(String key) {
        return null;
    }

    @Override
    public String getSessionId() {
        return null;
    }

    @Override
    public Object getSessionMutex() {
        return null;
    }
}

现在有RequestAttributes对象的实现类了,我们就尝试注入他,找到他的RequestAttributes对象注入的拦截器,重写他起名为FeignConfig,然后注入,下边是源码

@Component
@Slf4j
public class FeignConfig implements RequestInterceptor {
    public FeignConfig() {
    }
    @Bean
    public Retryer feignRetryer() {
        return  new Retryer.Default(100,1000,5);
    }
    @Bean
    public RequestContextListener requestContextListenerBean() {
        return new RequestContextListener();
    }
    @Override
    public void apply(RequestTemplate requestTemplate) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (Objects.nonNull(requestAttributes)){
            RequestContextHolder.setRequestAttributes(requestAttributes,true);
            try {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
                HttpServletRequest request = attributes.getRequest();
                requestTemplate.header(HttpHeaders.AUTHORIZATION, request.getHeader(HttpHeaders.AUTHORIZATION));
            }catch (Exception e){
                log.info("定时任务介入,授权异常");
            }

        }else {
            RequestContextHolder.setRequestAttributes(new NonWebRequestAttributes(), Boolean.TRUE);
            HttpServletRequest httpRequest = this.getHttpServletRequestSafely();
            if (null != httpRequest && null != httpRequest.getAttribute("X-Request-No")) {
                requestTemplate.header("X-Request-No", httpRequest.getAttribute("X-Request-No").toString());
            }
        }

    }

    public HttpServletRequest getHttpServletRequestSafely() {
        try {
            RequestAttributes requestAttributesSafely = this.getRequestAttributesSafely();
            return requestAttributesSafely instanceof NonWebRequestAttributes ? null : ((ServletRequestAttributes)requestAttributesSafely).getRequest();
        } catch (Exception var2) {
            return null;
        }
    }

    public RequestAttributes getRequestAttributesSafely() {
        RequestAttributes requestAttributes = null;
        try {
            requestAttributes = RequestContextHolder.currentRequestAttributes();
        } catch (IllegalStateException var3) {
            requestAttributes = new NonWebRequestAttributes();
        }

        return requestAttributes;
    }

可以看出我的apply方法里边写了if判断,是因为我需要在请求微服务的时候把token带到被调用的微服务,只要你的定时任务注入了我们自己写的NonWebRequestAttributes,那么这个定时任务会始终带着他,这也是我在在非web请求中使用Feign完成微服务调用(定时任务,过期策略等)_第2张图片
这个地方捉一下异常的原因,当然这些代码还可以简化或根据自己实际情况进行编写。

最后,你学废了吗?

你可能感兴趣的:(java,spring,boot,spring,cloud)