首页 > 解决方案 > 如何防止 Spring Boot Tomcat Jackson 中的 flushBuffer 并发锁定?

问题描述

从部署到 Tomcat 8 的 Spring Boot WAR 应用程序中写出 JSON 时,我遇到了并发问题。在 AppDynamics 的屏幕截图中,当杰克逊库执行 _flushBuffer 时,似乎需要等待相当长的时间。

即使是少量(< 10)用户的负载测试也会出现此问题。

我已经在我的配置类中配置了 messageConverters。

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new MappingJackson2HttpMessageConverter(
            new Jackson2ObjectMapperBuilder().dateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZ")).mixIn(LiquidAssignment.class,
                    InventoryProviderAssignmentMixin.class)
                    .deserializerByType(ActionData.class, new ActionDataDeserializer()).build()));
        converters.add(new MappingJackson2XmlHttpMessageConverter());
    }

我正在使用 Spring Boot 1.5.4 Java 1.8 Jackson 2.9.7 Tomcat 8.5.33

AppDynamics 截图

标签: javaspringspring-boottomcatjackson

解决方案


查看源代码UTF8JsonGenerator._flushBuffer()没有任何提示LockSupport.parkNanos()。所以它可能已经被 JIT 编译器从OutputStream.write().

我的猜测是,对于您的应用程序,Tomcat 通常会等待,直到客户端接受了所有输出(除了适合典型连接缓冲区大小的最后一部分),然后它才能关闭连接。

过去,我们对慢速客户有过不好的经历。在他们检索到所有输出之前,他们会阻塞 Tomcat 中的一个线程。在 Tomcat 中阻塞几十个线程会严重降低繁忙 Web 应用程序的吞吐量。

增加线程数并不是最好的选择,因为阻塞的线程也占用了大量的内存。所以你想要的是Tomcat可以尽快处理一个请求,然后继续下一个请求。

我们通过配置我们的反向代理解决了这个问题,我们总是在 Tomcat 前面使用它来立即消耗来自 Tomcat 的所有输出并以客户端的速度将其传递给客户端。反向代理在处理慢速客户端方面非常有效。

在我们的例子中,我们使用了nginx。我们还查看了Apache httpd。但在当时,它没有能力做到这一点。

附加说明

意外断开连接的客户端对于服务器来说也看起来像慢速客户端,因为它需要一些时间才能完全确定连接已断开。


推荐阅读