Java agent to detect blocking calls from non-blocking threads.
BlockHound will transparently instrument the JVM classes and intercept blocking calls (e.g. IO) if they are performed from threads marked as "non-blocking operations only" (ie. threads implementing Reactor's NonBlocking
marker interface, like those started by Schedulers.parallel()
). If and when this happens (but remember, this should never happen! 😜), an error will be thrown. Here is an example:
// Example.java
BlockHound.install();
Mono.delay(Duration.ofSeconds(1))
.doOnNext(it -> {
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
})
.block();
Will result in:
reactor.blockhound.BlockingOperationError: Blocking call! java.lang.Thread.sleep
at java.base/java.lang.Thread.sleep(Native Method)
at com.example.Example.lambda$exampleTest$0(Example.java:16)
Note that it points to the exact place where the blocking call got triggered. In this example it was Example.java:16
.
Download it from Maven Central repositories (stable releases only) or repo.spring.io:
Gradle
repositories {
mavenCentral()
// maven { url 'https://repo.spring.io/milestone' }
// maven { url 'https://repo.spring.io/snapshot' }
}
dependencies {
testImplementation 'io.projectreactor.tools:blockhound:$LATEST_RELEASE'
// testImplementation 'io.projectreactor.tools:blockhound:$LATEST_MILESTONE'
// testImplementation 'io.projectreactor.tools:blockhound:$LATEST_SNAPSHOT'
}
with Kotlin DSL
repositories {
mavenCentral()
// maven("https://repo.spring.io/milestone")
// maven("https://repo.spring.io/snapshot")
}
dependencies {
testImplementation("io.projectreactor.tools:blockhound:$LATEST_RELEASE")
// testImplementation("io.projectreactor.tools:blockhound:$LATEST_MILESTONE")
// testImplementation("io.projectreactor.tools:blockhound:$LATEST_SNAPSHOT")
}
Maven
<dependencies>
<dependency>
<groupId>io.projectreactor.tools</groupId>
<artifactId>blockhound</artifactId>
<version>$LATEST_RELEASE</version>
</dependency>
</dependencies>
Where:
$LATEST_RELEASE |
|
$LATEST_MILESTONE |
|
$LATEST_SNAPSHOT |
for JDK 13+, it is no longer allowed redefining native methods. So for the moment, as a temporary work around, please use the
-XX:+AllowRedefinitionToAddDeleteMethods
jvm argument:
Maven
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<argLine>-XX:+AllowRedefinitionToAddDeleteMethods</argLine>
</configuration>
</plugin>
Gradle
tasks.withType(Test).all {
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_13)) {
jvmArgs += [
"-XX:+AllowRedefinitionToAddDeleteMethods"
]
}
}
with Kotlin DSL
tasks.withType<Test>().all {
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_13)) {
jvmArgs("-XX:+AllowRedefinitionToAddDeleteMethods")
}
}
Although BlockHound supports the SPI mechanism to integrate with, it comes with a few built-in integrations:
- Project Reactor
Version 3.2.x is supported out of the box.
Starting withreactor-core
version 3.3.0, there is a built-in integration in Reactor itself that uses the SPI. - RxJava 2 is supported.
RxJava 3 and further versions of RxJava will require an SPI to be implemented, either by the framework or user. See this PR to RxJava with an example of the SPI's implementation.
See the docs.
Licensed under Apache Software License 2.0
Sponsored by Pivotal