Рекомендации по сопрограмме в приложении Spring

#spring #spring-boot #kotlin-coroutines

#весна #пружинный ботинок #котлин-сопрограммы

Вопрос:

Я хотел бы понять, каковы наилучшие методы использования сопрограмм kotlin в серверном приложении Spring, в частности, в отношении CoroutineContext . Допустим, у меня есть Controller такой:

 @Controller class ProductController {   @Autowired  lateinit var productRepository: ProductRepository   @Autowired  lateinit var mapper: ProductDtoMapper    @GetMapping("/")  suspend fun getProduct(): ProductDTO {  val product = productRepository.getFirst()  val productDTO = mapper.mapToDto(product)  return productDto  } }  

Тогда мое Repository похоже на:

 @Repository class ProductRepository(private val productService: ProductService) {   suspend fun getFirst(): Product = withContext(Dispatchers.IO) {  return productService.get()  } }  

Я заметил , что код внутри getProduct функции выполняется в CoroutineContext то есть Unconfined , что означает, что в приведенном выше примере после productRepository.getFirst() возврата выполнение возобновляется в том же потоке, определенном в ProductRepository.getFirst методе.

Теперь, в этом конкретном примере, вероятно, это не имеет значения, но я подумал, что с ростом приложения это может вызвать некоторые проблемы. Например, предположим, что ProductService выполняется несколько операций, блокирующих поток: в этом случае я мог бы решить создать фиксированное количество потоков, чтобы ограничить количество потоков для обработки такого рода операций. Код может выглядеть примерно так:

 @Repository class ProductRepository(private val productService: ProductService) {    private val dispatcher = Executors.newFixedThreadPool(10).asCoroutineDispatcher()   suspend fun getFirst(): Product = withContext(dispatcher) {  return productService.get()  } }  

Таким образом, как указано выше, после productRepository.getFirst() возврата выполнение возобновляется в том же потоке, определенном в ProductRepository.getFirst методе. Это означает, что также сопоставление ProductController будет выполняться в том же потоке, и если это также операция блокировки, поток продолжит выполнять операции, которые не предполагалось выполнять.

I come from Android and this kind of behavior is not recommended, since it might end up executing code that is doing cpu intensive operations in the main thread and this will be blocking the ui.

So, back to the server application, I would define the CoroutineContext that I want to use in the ProductController so I will be sure that the code after productRepository.getFirst() will be executed in the same context that I decided to be. It would look to something like this:

 @Controller class ProductController {   // other code    @GetMapping("/")  suspend fun getProduct(): ProductDTO = withContext(Dispatchers.Default) {  val product = productRepository.getFirst()  val productDTO = mapper.mapToDto(product)  return productDto  } }  

Таким образом, мне стало проще отслеживать контекст, в котором выполняется код.

Поэтому мои вопросы таковы:

  • Можно ли использовать этот Unconfined контекст?
  • Может ли такое поведение быть проблемой при необходимости масштабирования приложения?
  • Что является хорошей практикой для этого случая?

Если я написал что-то не так или мой вопрос неясен, дайте мне знать.