[spring-projects/spring-boot]OpenEntityManagerInViewFilter + StreamingResponseBody:连接泄漏(HikariCP)

2024-04-11 131 views
1

你好,

我遇到了 HikariCP 泄漏,我认为是由我的代码引起的,但看来是使用StreamingResponseBody本身造成的。

删除所有不需要的代码后,我得到了一个非常简单的复制案例。

使用 时StreamingResponseBody,一个连接在 CP 中永远保持活动状态。此连接在我的过滤器之一中打开,OncePerRequestFilter以填充请求范围的组件。

[更新]请参阅复制场景的下一条评论(包括项目 zip)。

在控制器中我直接调用此服务函数:

    @Override
    @Transactional
    public ResponseEntity<StreamingResponseBody> download() {

        StreamingResponseBody stream = (o) -> {}

        HttpHeaders headers = new HttpHeaders();

        return new ResponseEntity(stream, headers, HttpStatus.OK);

    }
com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - After adding stats (total=6, active=1, idle=5, waiting=0)

当我将响应主体的类型替换为 String 时,一切正常:

    @Override
    @Transactional
    public ResponseEntity download() {

        HttpHeaders headers = new HttpHeaders();

        return new ResponseEntity("", headers, HttpStatus.OK);

    }
com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - After cleanup  stats (total=5, active=0, idle=5, waiting=0)

这是我的 pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.someproject</groupId>
    <artifactId>central</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>central</name>
    <description>Central service for someproject Project</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    </properties>

    <repositories>
        <repository>
            <id>repo.com.someproject.common-lib.releases</id>
            <name>AWS Release Repository</name>
            <url>s3://repo.com.someproject.common-lib/releases</url>
        </repository>
        <repository>
            <id>repo.com.someproject.common-lib.snapshots</id>
            <name>AWS Snapshot Repository</name>
            <url>s3://repo.com.someproject.common-lib/snapshots</url>
        </repository>
    </repositories>

    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>com.amazonaws</groupId>
          <artifactId>aws-java-sdk-bom</artifactId>
          <version>1.11.297</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-dependencies</artifactId>
              <version>${spring-cloud.version}</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!-- For HTTP Endpoints -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- For Model -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <version>5.1.2.RELEASE</version>
            <scope>test</scope>
        </dependency>

        <!--For Mail -->
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>

        <!-- For database migration -->
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
        </dependency>

        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-s3</artifactId>
        </dependency>

        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-sns</artifactId>
        </dependency>

        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.11</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.modelmapper</groupId>
            <artifactId>modelmapper</artifactId>
            <version>2.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
            <version>1.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.someproject</groupId>
            <artifactId>common-lib</artifactId>
            <version>0.0.6-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.flywaydb.flyway-test-extensions</groupId>
            <artifactId>flyway-spring-test</artifactId>
            <version>5.2.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <finalName>legacy-service</finalName>
        <extensions>
            <extension>
                <groupId>org.springframework.build</groupId>
                <artifactId>aws-maven</artifactId>
                <version>5.0.0.RELEASE</version>
            </extension>
        </extensions>
    </build>

</project>

一些配置:

@Configuration
@EnableTransactionManagement
public class AppConfig implements WebMvcConfigurer {

    @Bean(name = "uploadTaskExecutor")
    public TaskExecutor specificTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(4);
        executor.setThreadNamePrefix("Upload-");
        executor.initialize();
        return executor;
    }

    /**
     * Fix: submitting a multipart request (multipart/form-data) using PUT.
     * Normally, Spring just only support POST for submitting a multipart request.
     */
    @Bean
    public MultipartResolver multipartResolver() {
        return new StandardServletMultipartResolver() {
            @Override
            public boolean isMultipart(HttpServletRequest request) {
                String method = request.getMethod().toLowerCase();
                //By default, only POST is allowed. Since this is an 'update' we should accept PUT.
                if (!Arrays.asList("put", "post").contains(method)) {
                    return false;
                }
                String contentType = request.getContentType();
                return (contentType != null &&contentType.toLowerCase().startsWith("multipart/"));
            }
        };
    }

    @Bean
    JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }

    @Bean
    public FilterRegistrationBean registerOpenSessionInViewFilterBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        OpenEntityManagerInViewFilter filter = new OpenEntityManagerInViewFilter();
        registrationBean.setFilter(filter);
        registrationBean.setOrder(5);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean requestContextFilterRegistration(){
        FilterRegistrationBean filter = new FilterRegistrationBean();
        filter.setFilter(requestContextFilter());
        filter.setOrder(0);
        return filter;
    }

    @Bean
    public RequestContextFilter requestContextFilter(){
        return new RequestContextFilter();
    }

    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("message/error_messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.defaultContentType(MediaType.APPLICATION_XML);
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        //TODO: to modify after other dev
       registry.addMapping("/**").allowedOrigins("*").allowedMethods("PUT", "DELETE","PATCH","GET","POST");
    }
}
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Profile("!test")
public class ResourceServerConfiguration extends BaseResourceServerConfiguration {

    @Value("${auth-server.url}")
    private String authEndpoint;

    @Value("${security.oauth2.client.client-id}")
    private String clientId;

    @Value("${security.oauth2.client.client-secret}")
    private String clientSecret;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("ms/legacy");
    }

    @Bean
    public ResourceServerTokenServices tokenService() {
        RemoteTokenServices tokenServices = new RemoteTokenServices();
        tokenServices.setClientId(clientId);
        tokenServices.setClientSecret(clientSecret);
        tokenServices.setCheckTokenEndpointUrl(authEndpoint + "/uaa/oauth/check_token");
        return tokenServices;
    }
}

我的 yml 道具:


server:
  port: 8086
  servlet:
    context-path: /
spring:
  profiles: local
  application:
    name: legacy-central-service
  cloud:
    config:
      allow-override: true
      override-none: true
  zipkin:
    enabled: true
    base-url: http://localhost:8087/
  sleuth:
    sampler:
      probability: 1.0
    feign:
      enabled: true
  # JPA Configuration
  jpa:
    hibernate:
      ddl-auto: validate
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
  datasource:
    url: jdbc:mysql://localhost:3306/legacy_service?autoReconnect=true&useSSL=false
    username: username
    password: password
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      idle-timeout: 600000
      max-lifetime: 1800000
      leak-detection-threshold: 60000
  thymeleaf:
    cache: false
    mode: HTML
    encoding: UTF-8
  flyway:
    baseline-on-migrate: true

  # For Upload File - the max support file size is 500MB
  servlet:
    multipart:
      enabled: true
      max-file-size: 500MB
      max-request-size: 500MB
      location: /tmp
  http:
    multipart:
      enabled: false

proj:
  central:
    mail:
      from: contact@proj.com
      host: localhost
      port: 25
      username:
      password:
      debug: false
    video:
      renderscore_tool_location: /tmp

eureka:
  instance:
    hostname: localhost
    port: 8081
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${eureka.instance.port}/eureka/
  server:
    wait-time-in-ms-when-sync-empty: 3000

auth-server:
  url: http://localhost:8083

security:
  oauth2:
    client:
      client-id: proj
      client-secret: proj
    resource:
      user-info-uri: http://localhost:8082/uaa
  jackson:
    property-naming-strategy: SNAKE_CASE
    serialization:
      indent-output: true

authorization-service:
  ribbon:
    listOfServers: http://localhost:8083
legacy-central-service:
  ribbon:
    listOfServers: http://localhost:8086
static-data-service:
  ribbon:
    listOfServers: http://localhost:7071

回答

9

我删除了项目的大部分类,以便您可以轻松地重现并仅检查相关代码。

样本错误.zip

编辑:泄漏是由于使用OpenEntityManagerInViewFilter时未能释放连接造成的。StreamingResponseBody评论它可以AppConfig.java解决泄漏问题。不过,我需要这个过滤器:P

为了能够启动该应用程序,您必须test_schema在本地 MySQL 数据库中创建一个空架构(在 中配置您的连接application.yml)。

  • 向 发出 GET 请求http://localhost:8086/ws3/file/data/?file=ANY_STRING
  • 该类GeneralFilter是唯一使用 JPA 并调用 的类MyUserRepository,它是唯一要求 HikaryCP 连接的地方。
  • 你会看到 hikary 日志:
2019-01-27 18:54:01.350 DEBUG 18739 --- [l-1 housekeeper] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Before cleanup stats (total=6, active=1, idle=5, waiting=0)
2019-01-27 18:54:01.351 DEBUG 18739 --- [l-1 housekeeper] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - After cleanup  stats (total=6, active=1, idle=5, waiting=0)

然后进行泄漏检测:

java.lang.Exception: Apparent connection leak detected
    at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:128) ~[HikariCP-2.7.9.jar:na]
    at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:35) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:106) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:136) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.connection(StatementPreparerImpl.java:47) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:145) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:171) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:147) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1985) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1915) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1893) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.loader.Loader.doQuery(Loader.java:938) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:341) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2692) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.loader.Loader.doList(Loader.java:2675) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2507) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.loader.Loader.list(Loader.java:2502) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:502) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:392) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:216) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1490) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1445) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1414) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.query.internal.AbstractProducedQuery.getSingleResult(AbstractProducedQuery.java:1463) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.hibernate.query.criteria.internal.compile.CriteriaQueryTypeQueryAdapter.getSingleResult(CriteriaQueryTypeQueryAdapter.java:107) ~[hibernate-core-5.2.17.Final.jar:5.2.17.Final]
    at org.springframework.data.jpa.repository.query.JpaQueryExecution$SingleEntityExecution.doExecute(JpaQueryExecution.java:214) ~[spring-data-jpa-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:91) ~[spring-data-jpa-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:136) ~[spring-data-jpa-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:125) ~[spring-data-jpa-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:590) ~[spring-data-commons-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:578) ~[spring-data-commons-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) ~[spring-data-commons-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) ~[spring-tx-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) ~[spring-tx-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135) ~[spring-data-jpa-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) ~[spring-data-commons-2.0.8.RELEASE.jar:2.0.8.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at com.sun.proxy.$Proxy96.findFirstByUuid(Unknown Source) ~[na:na]
    at com.myproject.filter.GeneralFilter.addRequestInfo(GeneralFilter.java:39) ~[classes/:na]
...

您可以尝试将ResponseEntity主体类型更改为String并返回空字符串来修复泄漏。

6

我对这个错误报告感到抱歉,事实上,@Async在我的服务功能上添加注释解决了这个问题,所以这不是一个错误,我的错!

2

好吧,它确实可以解决泄漏问题,但任何真正的 IO 传输也会失败(尝试流式传输主体中的任何资源来查看它)。如此糟糕的解决方案,仍然是一个错误,对于过早关闭表示抱歉!

8

感谢您提供的示例,但还有很多内容。如果您有时间进一步精简它,包括用内存数据库(H2 或 HSQLDB)替换对 MySQL 的需求,这将增加有时间调查的人的更改。

3

我很抱歉成为那个人,但我更愿意直接告诉你,我无法花时间来增强样本。

1

该问题是由错误配置引起的OpenEntityManagerInViewFilter。它的调度程序类型尚未自定义,因此仅在请求调度时调用,而不会在异步调度时调用。因此,它打开的会话永远不会关闭,从而导致数据库连接泄漏。

3

那么这个问题解决了吗?StreamingRequestBody与 JPA 一起使用时仍然存在 CP 泄漏。不确定,因为状态invalid已应用于此问题。

4

@alexpartsch 据我们所知,Spring Boot 中没有什么需要修复的,因为问题是由应用程序中的配置错误引起的。有关详细信息,请参阅您上面的我的评论

3

@wilkinsona我读了你的评论,但假设 Spring Boot 处理线程的部分StreamingRequestBody必须“自定义调度程序类型”(由于我缺乏理解,OpenEntityManagerInViewFilter我不知道这些调度程序类型是什么以及它们在哪里)可以自定义它们,但现在想找出答案。),但据我从您最后的评论中了解到,框架用户可能负责这一点。

5

调度程序类型是 servlet 规范的一部分,它们控制调用过滤器的调度类型。当您使用StreamingResponseBody异步调度时,您需要确保过滤器已注册用于异步调度,否则一旦发送响应,它将没有机会正确清理。您可以使用 来配置调度程序类型FilterRegistrationBean。请注意,如果您使用的是 Spring Boot 2.3,由于https://github.com/spring-projects/spring-boot/issues/18953OpenEntityManagerInViewFilter中所做的更改,默认OncePerRequestFilter情况下将注册异步调度。

8

感谢您的信息!我明白了更知道了。

我正在运行 Spring Boot 2.3.3,但似乎没有OpenEntityManagerInViewFilter注册或至少在任何请求上执行。我尝试自己定义它,但LazyInitialisationException由于异步请求中没有打开会话而失败。这是我的过滤器 bean 定义:

    @Bean
    @Primary
    public FilterRegistrationBean<OpenEntityManagerInViewFilter> openEntityManagerInViewFilterFilterRegistrationBean() {
        var filter = new OpenEntityManagerInViewFilter();
        var registrationBean = new FilterRegistrationBean<>(filter);
        registrationBean.setAsyncSupported(true);
        registrationBean.addUrlPatterns("/*");
        registrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
        return registrationBean;
    }

我使用休眠的默认EntityManagerFactory配置,所以我认为我不需要配置更多? ……不过好像还是没有执行。既然你说 2.3 已经将其定义为 ASNY,那么我的泄漏可能是由其他原因引起的?

1

听起来确实是这样。如果您可以向我们提供一个重现泄漏的最小示例项目,请打开一个新问题,我们可以看一下。

2

好的,按照上面的描述定义我的 Bean 后,由于我的事务管理而出现了另一个错误。解决了这个问题,新的OpenEntityManagerInViewFilter现在似乎工作得很好。您确定DispatcherType.ASYNCSpring Boot 2.3.3 中默认启用该功能吗?

0

好的,找到了问题,仅供进一步参考:当使用 和 进行测试时,MockMvc应该StreamingRequestBody使用 来asyncDispatch RequestBuilder检索和正确处理响应(请参阅此处)。我删除了OpenEntityManagerInViewFilter过滤器注册,它仍然有效?‍♂️

感谢@wilkinsona 的帮助!