Error (17) solves the problem of losing the request header of multithread asynchronous feign call

questions

In microservice, the request header will be lost in multithreading asynchronous + feign call

solving
    1. in the main thread, the request header parameters are first obtained and passed into the sub thread, and then the sub thread sets the request header parameters to the context. Finally, in feign forwarding processing, the request header data of the context set by the sub thread is obtained and forwarded to the downstream

Get context request parameter tool class

@Slf4j
public class RequestContextUtil {

    /**
     * Get request header data
     *
     * @return key-> request header name value-> request header value
     * @author zhengqingya
     * @date 2021/6/30 9:39 PM
     */
    public static Map<String, String> getHeaderMap() {
        Map<String, String> headerMap = Maps.newLinkedHashMap();
        try {
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (requestAttributes == null) {
                return headerMap;
            }
            HttpServletRequest request = requestAttributes.getRequest();
            Enumeration<String> enumeration = request.getHeaderNames();
            while (enumeration.hasMoreElements()) {
                String key = enumeration.nextElement();
                String value = request.getHeader(key);
                headerMap.put(key, value);
            }
        } catch (Exception e) {
            log.error("《RequestContextUtil》 Failed to get request header parameters:", e);
        }
        return headerMap;
    }

}

Request header context

@Slf4j
public class RequestHeaderHandler {

    public static final ThreadLocal<Map<String, String>> THREAD_LOCAL = new ThreadLocal<>();

    public static void setHeaderMap(Map<String, String> headerMap) {
        THREAD_LOCAL.set(headerMap);
    }

    public static Map<String, String> getHeaderMap() {
        return THREAD_LOCAL.get();
    }

    public static void remove() {
        THREAD_LOCAL.remove();
    }

}

Feign forwarding process RPC call parameter transfer

 */
@Slf4j
@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {

    @Override
    @SneakyThrows
    public void apply(RequestTemplate requestTemplate) {
        log.debug("========================== ↓↓↓↓↓↓ 《FeignRequestInterceptor》 Start... ↓↓↓↓↓↓ ==========================");
        Map<String, String> threadHeaderNameMap = RequestHeaderHandler.getHeaderMap();
        if (!CollectionUtils.isEmpty(threadHeaderNameMap)) {
            threadHeaderNameMap.forEach((headerName, headerValue) -> {
                log.debug("《FeignRequestInterceptor》 Multi-threaded headerName:[{}] headerValue:[{}]", headerName, headerValue);
                requestTemplate.header(headerName, headerValue);
            });
        }
        Map<String, String> headerMap = RequestContextUtil.getHeaderMap();
        headerMap.forEach((headerName, headerValue) -> {
            log.debug("《FeignRequestInterceptor》 headerName:[{}] headerValue:[{}]", headerName, headerValue);
            requestTemplate.header(headerName, headerValue);
        });
        log.debug("========================== ↑↑↑↑↑↑ 《FeignRequestInterceptor》 End... ↑↑↑↑↑↑ ==========================");
    }

}

Use cases

@Slf4j
@RestController
@RequestMapping("/web/api/demo/test")
@Api(tags = "test api")
@AllArgsConstructor
public class RpcController extends BaseController {

    private SystemTaskThread systemTaskThread;

    @GetMapping("getContextUserId")
    @ApiOperation("rpc Call test - Async")
    public void getContextUserId() {
        Map<String, String> headerMap = RequestContextUtil.getHeaderMap();
        log.info("Main thread request header value: {}", headerMap.get("userId"));
        this.systemTaskThread.getRequestHeaderUserId(RequestContextUtil.getHeaderMap());
    }

}

@Slf4j
@Component
@AllArgsConstructor
public class SystemTaskThread {

    private ISystemClient systemClient;

    @SneakyThrows
    @Async(ThreadPoolConstant.SMALL_TOOLS_THREAD_POOL)
    public void getRequestHeaderUserId(Map<String, String> headerMap) {
        RequestHeaderHandler.setHeaderMap(headerMap);
        log.info("Sub-thread request header value: {}", this.systemClient.getRequestHeaderUserId());
    }

}

Note: it is also mentioned on the Internet that the main thread obtains the request parameters requestattributes, requestattributes = requestcontextholder. Getrequestattributes() to the child thread, and then re assign requestcontextholder. Setrequestattributes (requestattributes) But this method is not effective. Please record it here by the way~


Read More: