Webflux a BlockHound

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

Napsat komentář