BlockHound je java agent (software nahraný JVM před zavoláním main metody), který způsobí to, že pokud v rámci neblokujícího vlákna (vlákno, které nemá volat blokující volání) zavoláte metodu, která blokuje vlákno, vyhodí se chyba.
Závislost
implementation("io.projectreactor.tools:blockhound:1.0.4.RELEASE")
Pokud si chceme BlockHound přizpůsobit, vytvoříme třídu, která implementuje BlockHoundIntegration
import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.stereotype.Component import reactor.blockhound.BlockHound import reactor.blockhound.BlockingOperationError import javax.annotation.PostConstruct import reactor.blockhound.integration.BlockHoundIntegration @Component class BlockHoundIntegration : BlockHoundIntegration { private val logger: Logger = LoggerFactory.getLogger(javaClass) @PostConstruct fun init() { BlockHound.install(this) } override fun applyTo(builder: BlockHound.Builder) { builder.blockingMethodCallback { logger.error("Blocking Call!", BlockingOperationError(it)) } } }
Když nyní zavoláme některý z těchto endpointů
@GetMapping("/blockV1") fun blockV1(): Mono { return Mono.just("Ok") .map { Thread.sleep(1000L) it } } @GetMapping("/blockV2") fun blockV2(): Mono { logger.info("GET /block called") Thread.sleep(100L) return Mono.just("Ok") }
v logu budeme mít chybu
reactor.blockhound.BlockingOperationError: Blocking call! java.lang.Thread.sleep at cz.vitfo.webfluxpostgres.blockhound.BlockHoundIntegration$applyTo$1.accept(BlockHoundIntegration.kt:22) ~[main/:na] at cz.vitfo.webfluxpostgres.blockhound.BlockHoundIntegration$applyTo$1.accept(BlockHoundIntegration.kt:12) ~[main/:na] at reactor.blockhound.BlockHound$Builder.lambda$install$8(BlockHound.java:383) ~[blockhound-1.0.4.RELEASE.jar:na] at reactor.blockhound.BlockHoundRuntime.checkBlocking(BlockHoundRuntime.java:89) ~[na:na] at java.base/java.lang.Thread.sleep(Thread.java) ~[na:na]
Pokud chceme aby, program chybu nejen zalogoval, ale aby se propagovala výše, stačí upravit metodu applyTo BlockHoundIntegration
override fun applyTo(builder: BlockHound.Builder) { builder.blockingMethodCallback { val error = BlockingOperationError(it) logger.error("Blocking Call!", error) throw error } }
Nyní volání endpointu vrátí
{ "timestamp": "2021-03-30T07:25:36.233+0000", "path": "/blockV1", "status": 500, "error": "Internal Server Error", "message": "Blocking call! java.lang.Thread.sleep" }
Zdroj: domenicosibilio.medium.com/blockhound-detect-blocking-calls-in-reactive-code-before-its-too-late