在基于Reactor的应用程序上执行阻塞性调用
Project Reactor是一个完全非阻塞的基础,包括背压支持。虽然外面的大多数库都支持异步方法,从而协助其使用,但在某些情况下,一个库包含复杂的阻塞方法而没有异步实现。在反应器流中调用这些方法会产生不好的结果。相反,我们需要把这些方法变成异步的,或者找到一个变通的办法。
如果你的时间很短,不可能给所使用的工具打补丁,或者你不能确定如何逆向设计阻塞式调用并实现非阻塞式版本,那么利用一些线程是有意义的。
首先,让我们为我们的项目导入依赖项
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId>
<version>2020.0.11</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
让我们从阻塞式服务开始
public String get(String url) throws IOException {
HttpURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(true);
try(InputStream inputStream = connection.getInputStream()) {
return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
}
}
我们使用了HttpsURLConnection,因为我们知道它是一个阻塞式调用。为此我们需要一个Scheduler。对于阻塞式调用,我们将使用boundedElastic调度器。一个调度器也可以由现有的执行器服务来创建。
所以让我们把这个方法转化为非阻塞的方法:
package com.gkatzioura.blocking;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
public class BlockingAsyncService {
private final BlockingService blockingService;
public BlockingAsyncService(BlockingService blockingService) {
this.blockingService = blockingService;
}
private Mono<String> get(String url) {
return Mono.fromCallable(() -> blockingService.get(url))
.subscribeOn(Schedulers.boundedElastic());
}
}
我们可以看到的是一个由可调用方法创建的Mono。一个调度器订阅了这个单体,因此将收到发出的事件,这些事件应被安排执行。
让我们进行一次测试
package com.gkatzioura.blocking;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
class BlockingAsyncServiceTest {
private BlockingAsyncService blockingAsyncService;
@BeforeEach
void setUp() {
blockingAsyncService = new BlockingAsyncService(new BlockingService());
}
@Test
void name() {
StepVerifier.create(
Mono.just("https://www.google.com/")
.map(s -> blockingAsyncService.get(s))
.flatMap(s -> s)
)
.consumeNextWith(s -> s.startsWith("<!doctype"))
.verifyComplete();
}
}
就这样吧!显然,最好的办法是找到一种方法,将这个阻塞调用变成异步调用,并尝试使用外面的异步库找到一个变通的方法。当它不可行的时候,我们可以使用Threads。