#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
контекст? - Может ли такое поведение быть проблемой при необходимости масштабирования приложения?
- Что является хорошей практикой для этого случая?
Если я написал что-то не так или мой вопрос неясен, дайте мне знать.