【发布时间】:2020-03-15 00:55:19
【问题描述】:
我有一个包含在 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 都是事务性的,感谢:
@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)
}
}
}
【问题讨论】:
-
您没有交易。您的服务必须是
@Transactional才能正常工作。 -
是的,对不起,我忘了解释他们是一个捕手,它会自动将
@Transactionnal注释添加到我所有的@Service。只是为了确保我在我的服务上添加了注释,但这并没有改变任何东西..
标签: postgresql spring-boot kotlin hikaricp jdbi