你好,
我遇到了 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