线程在Jackson的SerializerCache中被阻止

时间:2016-06-22 20:57:05

标签: multithreading spring-boot jackson

当我遇到一个奇怪的行为时,我正在加载测试我的Spring Boot 1.3.5应用程序。在增加请求数量(例如,5000个请求/秒)后,线程开始阻止杰克逊的SerializerCache。起初我认为这可能是我身边的一些错误配置,所以我创建了一个新的,最小的应用程序:

@SpringBootApplication
public class DojoRestApplication {
  public static void main(String[] args) {
    SpringApplication.run(DojoRestApplication.class, args);
  }

  @RestController
  public static final class StatusController {
    @RequestMapping(method = GET, path = "/status", produces = "application/json")
    public ResponseEntity<ApplicationStatus> get() {
      final ApplicationStatus result = new ApplicationStatus();
      result.setTimestamp(Instant.now());
      result.setVersion("1.0.0");
      return ResponseEntity.ok(result);
    }
  }

  public static final class ApplicationStatus {
    private Instant timestamp;
    private String version;

    public Instant getTimestamp() {
      return timestamp;
    }

    public String getVersion() {
      return version;
    }

    public void setTimestamp(Instant timestamp) {
      this.timestamp = timestamp;
    }

    public void setVersion(String version) {
      this.version = version;
    }
  }
}

/status端点抛出一堆同时请求时,这是JProfiler告诉我的:

JProfiler

我接受了一个线程转储,几乎所有的线程都被卡住了:

com.fasterxml.jackson.databind.ser.SerializerCache.untypedValueSerializer(java.lang.Class) (line: 84)
com.fasterxml.jackson.databind.SerializerProvider._findExplicitUntypedSerializer(java.lang.Class) (line: 1124)
com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.hasSerializerFor(java.lang.Class, java.util.concurrent.atomic.AtomicReference) (line: 422)
com.fasterxml.jackson.databind.ObjectMapper.canSerialize(java.lang.Class, java.util.concurrent.atomic.AtomicReference)
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.canWrite(java.lang.Class, org.springframework.http.MediaType) (line: 178)
org.springframework.http.converter.AbstractGenericHttpMessageConverter.canWrite(java.lang.reflect.Type, java.lang.Class, org.springframework.http.MediaType)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(java.lang.Object, org.springframework.core.MethodParameter, org.springframework.http.server.ServletServerHttpRequest, org.springframework.http.server.ServletServerHttpResponse) (line: 215)
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(java.lang.Object, org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest) (line: 183)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(java.lang.Object, org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest) (line: 81)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(org.springframework.web.context.request.ServletWebRequest, org.springframework.web.method.support.ModelAndViewContainer, java.lang.Object[ ]) (line: 126)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.web.method.HandlerMethod) (line: 832)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.web.method.HandlerMethod) (line: 743)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
org.springframework.web.servlet.DispatcherServlet.doDispatch(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) (line: 961)
org.springframework.web.servlet.DispatcherServlet.doService(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) (line: 895)
org.springframework.web.servlet.FrameworkServlet.processRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) (line: 967)
org.springframework.web.servlet.FrameworkServlet.doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) (line: 858)
javax.servlet.http.HttpServlet.service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) (line: 622)
org.springframework.web.servlet.FrameworkServlet.service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) (line: 843)
javax.servlet.http.HttpServlet.service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 729)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 292)
org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 207)
org.apache.tomcat.websocket.server.WsFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) (line: 52)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 240)
org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 207)
org.springframework.web.filter.RequestContextFilter.doFilterInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain) (line: 99)
org.springframework.web.filter.OncePerRequestFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) (line: 107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 240)
org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 207)
org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain) (line: 87)
org.springframework.web.filter.OncePerRequestFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) (line: 107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 240)
org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 207)
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain) (line: 77)
org.springframework.web.filter.OncePerRequestFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) (line: 107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 240)
org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 207)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain) (line: 121)
org.springframework.web.filter.OncePerRequestFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) (line: 107)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 240)
org.apache.catalina.core.ApplicationFilterChain.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) (line: 207)
org.apache.catalina.core.StandardWrapperValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) (line: 212)
org.apache.catalina.core.StandardContextValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) (line: 106)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) (line: 502)
org.apache.catalina.core.StandardHostValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) (line: 141)
org.apache.catalina.valves.ErrorReportValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) (line: 79)
org.apache.catalina.core.StandardEngineValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) (line: 88)
org.apache.catalina.connector.CoyoteAdapter.service(org.apache.coyote.Request, org.apache.coyote.Response) (line: 522)
org.apache.coyote.http11.AbstractHttp11Processor.process(org.apache.tomcat.util.net.SocketWrapper) (line: 1095)
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(org.apache.tomcat.util.net.SocketWrapper, org.apache.tomcat.util.net.SocketStatus) (line: 672)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun() (line: 1502)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run() (line: 1458)
java.util.concurrent.ThreadPoolExecutor.runWorker(java.util.concurrent.ThreadPoolExecutor$Worker) (line: 1142)
java.util.concurrent.ThreadPoolExecutor$Worker.run() (line: 617)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run() (line: 61)
java.lang.Thread.run() (line: 745)

我发现这种情况似乎很奇怪,因为看起来Jackson正在为每一个请求创建一个序列化器。什么可能导致这种行为?这是在杰克逊或Spring Boot的一方(或我的,虽然我没有真正改变任何配置)?使用的Jackson版本是2.6.6(Spring Boot 1.3.5的默认值)。

1 个答案:

答案 0 :(得分:1)

杰克逊没有为每个请求创建新的序列化程序;该特定行是来自int& x的简单get查找的小同步块。让同步持续足够长的时间让线程对其进行竞争似乎很奇怪。所以我想知道这可能是一个分析工件。

但是:在第一个线程调用HashMap之后,不应该调用该方法本身;在初始查找成功之后,将序列化程序添加到共享映射中,但是构造的其他ObjectMapper应该获得具有新绑定序列化程序的只读副本。所以令人费解的是为什么它会被连续访问。

也许您可以在SerializerProvider问题跟踪器上提交问题? 2.6足够新,行为可能仍然存在于2.7中。