如何使用 Spring webflux 实现/迁移 OncePerRequestFilter

Mar*_*ijk 6 spring spring-boot spring-webflux

使用 Spring web 可以简单地OncePerRequestFilter(见下文)维护请求跨度的请求 id。将生成的请求 ID 存储在请求属性中,将其添加到日志记录 MDC,并在响应标头中返回。

我知道反应式 webflux 堆栈完全不同,那么应该如何解决这个问题呢?

我找到了https://github.com/spring-projects/spring-framework/issues/20239但不清楚现在支持或不支持什么。

@Component
public class RequestIdFilter extends OncePerRequestFilter implements Ordered {

    private static final String MDC_KEY = "requestId";
    private static final String REQUEST_ATTRIBUTE_NAME = "requestId";
    private static final String RESPONSE_HEADER_NAME = "X-Request-Id";


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        var requestId = UUID.randomUUID().toString();
        MDC.put(MDC_KEY, requestId);
        request.setAttribute(REQUEST_ATTRIBUTE_NAME, requestId);
        response.setHeader(RESPONSE_HEADER_NAME, requestId);
        try {
            filterChain.doFilter(request, response);
        } finally {
            MDC.remove(MDC_KEY);
        }
    }

    @Override
    public int getOrder() {
        return requestIdProperties.getServerFilterOrder();
    }
}
Run Code Online (Sandbox Code Playgroud)

Bri*_*zel 5

您不需要OncePerRequestFilter在 WebFlux 中实现,因为过滤器仅执行一次,因为 WebFlux 不支持请求转发(如 Servlet 中)。

现在您可以实现WebFilter将 requestId 添加为请求属性,与您显示的版本非常相似。

有以下几点需要注意:

  • 您应该避免在反应式管道中调用阻塞方法,UUID.randomUUID() 正在阻塞
  • 在反应式环境中向 MDC 添加数据并不简单,因为此功能最初依赖于ThreadLocal. 暂时查看这篇博文并关注此问题以获取更多指导
  • 考虑到这个用例,听起来Spring Cloud Sleuth可能会实现你想要的,甚至更多(支持跨度等)。