Spring 启用 SSL 的引导阻塞连接池
Spring Boot Blocked Connection Pool with enabled SSL
我有一个 Spring 启动应用程序(版本 2.5.3),它使用自签名证书启用了 SSL。
一个端点用于使用 StreamingResponseBody 在客户端下载文件。
问题
问题是,当用户通过此端点请求文件时,连接池不会被清理。
在此处展示问题的工作示例:
https://github.com/smotastic/blocked-connection-pool
@GetMapping("/ping")
public ResponseEntity<StreamingResponseBody> ping() {
// service.findSomeFile(...)
StreamingResponseBody body = new StreamingResponseBody() {
@Override
public void writeTo(OutputStream outputStream) throws IOException {
// outputStream.write(someFile);
}
};
return ResponseEntity.ok(body);
}
调用它并打开 Hikari 连接池的调试日志记录后,将记录以下内容。
Pool stats (total=10, active=1, idle=9, waiting=0)
如您所见,一个连接处于活动状态,并且永远不会再次释放。如果我再调用此端点 9 次,连接池已满,任何后续请求都将 运行 超时。
到目前为止我发现了什么
启用 ssl 的应用程序、对服务或存储库的调用以及 StreamingResponseBody 的 return 值的组合似乎是我的问题。
只要我关闭其中一个因素,问题就会消失,连接会按预期释放。
// SecurityConfig.java
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll();
http.requiresChannel().anyRequest().requiresSecure(); // turning this off 'solves' the problem
}
在示例中,我提供了一个简单的 FooEntity 和一个 FooRepository,我只是在其中调用了 count 方法。
我想这会打开一个新事务并在此处引发错误。
// Controller.java
@Autowired
FooRepository fooRepo;
@GetMapping("/ping")
public ResponseEntity<StreamingResponseBody> ping() {
fooRepo.count(); // removing this line 'solves' the error
StreamingResponseBody body = new StreamingResponseBody() {
@Override
public void writeTo(OutputStream outputStream) throws IOException {
// outStream.write(...)
}
};
return ResponseEntity.ok(body);
}
在这种情况下,FooRepository 只是 Spring 中的一个简单的 CrudRepository,没有额外的功能。
// FooRepository.java
@Repository
public interface FooRepository extends CrudRepository<Foo, Long>{
}
FooEntity 是一个带有 ID 的简单实体。
// Foo.java
@Entity
public class Foo {
@GeneratedValue
@Id
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
在这个例子中,我使用的是 h2 数据源,但我也用 postgres 尝试过。
两者都不起作用。
# application.properties
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
通过以下配置启用 ssl。
# https via self signed certificate
server.ssl.key-store-type=${SSL_KEY_STORE_TYPE:PKCS12}
# The path to the keystore containing the certificate
server.ssl.key-store=${SSL_KEY_STORE:classpath:identity.p12}
# The password used to generate the certificate
server.ssl.key-store-password=${SSL_KEY_STORE_PASSWORD:hello}
# The alias mapped to the certificate
server.ssl.key-alias=${SSL_KEY_ALIAS:mykey}
server.ssl.enabled=${SSL_ENABLED:true}
该证书是一个自签名证书,在我的 Windows 10 机器上创建。
此外,Hikari 连接池似乎不是问题所在。我还尝试了 Tomcat JDBC 连接池。池仍然被阻塞。
# application.properties
spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
尝试添加
spring.jpa.open-in-view=false
在属性中。
我有一个 Spring 启动应用程序(版本 2.5.3),它使用自签名证书启用了 SSL。 一个端点用于使用 StreamingResponseBody 在客户端下载文件。
问题
问题是,当用户通过此端点请求文件时,连接池不会被清理。 在此处展示问题的工作示例: https://github.com/smotastic/blocked-connection-pool
@GetMapping("/ping")
public ResponseEntity<StreamingResponseBody> ping() {
// service.findSomeFile(...)
StreamingResponseBody body = new StreamingResponseBody() {
@Override
public void writeTo(OutputStream outputStream) throws IOException {
// outputStream.write(someFile);
}
};
return ResponseEntity.ok(body);
}
调用它并打开 Hikari 连接池的调试日志记录后,将记录以下内容。
Pool stats (total=10, active=1, idle=9, waiting=0)
如您所见,一个连接处于活动状态,并且永远不会再次释放。如果我再调用此端点 9 次,连接池已满,任何后续请求都将 运行 超时。
到目前为止我发现了什么
启用 ssl 的应用程序、对服务或存储库的调用以及 StreamingResponseBody 的 return 值的组合似乎是我的问题。 只要我关闭其中一个因素,问题就会消失,连接会按预期释放。
// SecurityConfig.java
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll();
http.requiresChannel().anyRequest().requiresSecure(); // turning this off 'solves' the problem
}
在示例中,我提供了一个简单的 FooEntity 和一个 FooRepository,我只是在其中调用了 count 方法。 我想这会打开一个新事务并在此处引发错误。
// Controller.java
@Autowired
FooRepository fooRepo;
@GetMapping("/ping")
public ResponseEntity<StreamingResponseBody> ping() {
fooRepo.count(); // removing this line 'solves' the error
StreamingResponseBody body = new StreamingResponseBody() {
@Override
public void writeTo(OutputStream outputStream) throws IOException {
// outStream.write(...)
}
};
return ResponseEntity.ok(body);
}
在这种情况下,FooRepository 只是 Spring 中的一个简单的 CrudRepository,没有额外的功能。
// FooRepository.java
@Repository
public interface FooRepository extends CrudRepository<Foo, Long>{
}
FooEntity 是一个带有 ID 的简单实体。
// Foo.java
@Entity
public class Foo {
@GeneratedValue
@Id
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
在这个例子中,我使用的是 h2 数据源,但我也用 postgres 尝试过。 两者都不起作用。
# application.properties
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
通过以下配置启用 ssl。
# https via self signed certificate
server.ssl.key-store-type=${SSL_KEY_STORE_TYPE:PKCS12}
# The path to the keystore containing the certificate
server.ssl.key-store=${SSL_KEY_STORE:classpath:identity.p12}
# The password used to generate the certificate
server.ssl.key-store-password=${SSL_KEY_STORE_PASSWORD:hello}
# The alias mapped to the certificate
server.ssl.key-alias=${SSL_KEY_ALIAS:mykey}
server.ssl.enabled=${SSL_ENABLED:true}
该证书是一个自签名证书,在我的 Windows 10 机器上创建。
此外,Hikari 连接池似乎不是问题所在。我还尝试了 Tomcat JDBC 连接池。池仍然被阻塞。
# application.properties
spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
尝试添加
spring.jpa.open-in-view=false
在属性中。