gpt4 book ai didi

postgresql - Spring Boot 2.1.4 + JDBI + HikariCP + PostgreSQL 发生错误后连接未释放到池

转载 作者:行者123 更新时间:2023-12-02 13:34:49 27 4
gpt4 key购买 nike

我有一个包含在 API 中的 Spring Boot 应用程序。它暴露了一个端点 /api/persons/:id .
在引擎盖下 Spring Boot 带有 Hibernate 和 HikariCP。我的数据库是 PostgreSQL 10。
该应用程序是在 Kotlin 中构建的。

当 API 接收到同时请求时,我发现了一个问题,该应用似乎需要 2 个事件连接才能执行操作 findpersonById()由端点调用。

池是这样配置的:spring.datasource.hikari.maximumPoolSize=5
当我同时向 /api/persons/:id 发送 5 个请求时从池中取出5个连接来执行后面的请求,5个处于挂起状态。
池最终抛出异常,因为 5 个挂起的连接一直在等待 connectionTimeout 期间,请求也失败了。

我面临的问题是,在那之后 HikariCP 仍然说池中有 5 个事件连接。如果我查看 PostgreSQL 统计信息,所有连接都是空闲的。

仅供引用,如果我同时只发送 4 个请求,一切都按异常进行,它从 5 个事件连接和 3 个挂起开始,所有请求都返回异常结果。

我尝试将 Pool 更改为 Tomcat JDBC,但结果完全相同。

我不明白为什么首先需要 2 个连接。

如果有人知道我做错了什么,我会在这里传递一些代码。

@RestController
@RequestMapping("/api/person")
class PersonResource(private val personService: PersonService,
private val personUpdateService: PersonUpdateService,
private val identityManagementService: IdentityWithManagementService) : PersonApi {

@GetMapping("/{id}")
override fun findPersonById(@PathVariable id: String): PersonDto? {
return personService.findFull(id)
}

}
personService :
@Service
class PersonService(private val documentsService: DocumentService,
private val postalAddressService: PostalAddressService) : EntityService<Person>(repository) {

fun findFull(personId: String): PersonDto? {
return find(personId)?.let { person ->
PersonDto(
person,
postalAddressService.findByPersonId(personId).map { it.toDto() },
documentsService.findByPersonId(personId).map { it.toDto() }
)
}
}
}
PersonPostgresRepository :
@Repository
class PersonPostgresRepository : AbstractPostgresRepository(), PersonEntityRepository {

override fun find(id: String): Person? {
return withHandle<Person?, Exception> {
it.createQuery(
"select * " +
"from identiti_person " +
"where id = :id")
.bind("id", id)
.map(PersonRowMapper())
.firstOrNull()
}
}
}
AbstractPostgresRepository :
abstract class AbstractPostgresRepository {

@Autowired
private lateinit var handleManager: JdbiHandleManager

@Autowired
private lateinit var jdbi: Jdbi

protected fun <R, X : Exception> withHandle(callback: (handle: Handle) -> R): R {
val handle = handleManager.handle
return if (handle.isPresent) {
callback.invoke(handle.get())
} else {
jdbi.withHandle(HandleCallback<R, X> {
callback.invoke(it)
})
}
}
}

JdbiHandleManager如果你问:
@Component
@Scope("singleton")
class JdbiHandleManager(private val jdbi: Jdbi) {

private val currentHandle = ThreadLocal<Handle>()

val handle: Optional<Handle>
get() = Optional.ofNullable(currentHandle.get())

internal fun openHandle(): Handle {
val handle = jdbi.open()
currentHandle.set(handle)
return handle
}

internal fun closeHandle() {
val handle = currentHandle.get()
currentHandle.remove()
handle?.close()
}
}

JdbiConfig初始化 Jdbi:
@Configuration
open class JdbiConfig {

@Bean
open fun jdbi(dataSource: DataSource): Jdbi {
// JDBI wants to control the Connection wrap the datasource in a proxy
// That is aware of the Spring managed transaction
val dataSourceProxy = TransactionAwareDataSourceProxy(dataSource)
val jdbi = Jdbi.create(dataSourceProxy)
jdbi.installPlugins()

return jdbi
}
}

我所有的 @Service是transactionnal 感谢这个:
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
override fun transactionAttributeSource(): TransactionAttributeSource {
/*
Defines an annotation transaction source (the default) which consider that @Service components
are transactional by default (making the use of @Transactional optional on those classes).
Note that the class has to be processed by a TransactionInterceptor for that source to be applied,
this is the responsibility of the auto proxy creator below.
*/
return object : AnnotationTransactionAttributeSource() {
override fun findTransactionAttribute(clazz: Class<*>): TransactionAttribute? {
return if (clazz.getAnnotation(Service::class.java) != null && clazz.getAnnotation(Transactional::class.java) == null) {
DefaultTransactionAttribute(TransactionAttribute.PROPAGATION_REQUIRED)
} else super.findTransactionAttribute(clazz)
}
}
}

最佳答案

我没有运行您的代码,但我非常确信根本问题如下:

您的 JdbiHandleManager是多余的并导致问题(打开 2 个连接)。为什么?因为TransactionAwareDataSourceProxy已经处理了连接的打开和关闭。当 Spring 遇到“事务性”(通过注释或方面)的方法调用时,将打开连接并绑定(bind)到当前线程。

这意味着只使用 jdbi.withHandle(...) 就足够了。因为下一个打开连接的调用返回事件的事务连接并调用 close被代理,因为 spring 会自行关闭连接。

由于您实现了自己的“经理”,这会发生两次,一次是 Spring ,一次是您。两个线程本地连接(一个已经包裹在 Handle 中)

我这里的建议是去掉你的自定义代码,完全依赖TransactionAwareDataSourceProxy的正确操作

关于postgresql - Spring Boot 2.1.4 + JDBI + HikariCP + PostgreSQL 发生错误后连接未释放到池,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58935159/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com