我是 Vert.x 的新手
我的目标是使用 Rest API,我们称它为 CRUD-API,使用 VertX 提供的 Webclient 类。 CRUD-API 需要一个 ID 并在响应正文中返回一个 json 对象。
我的问题是我的 Consumer verticle 在调用 webclient 执行之前返回。
这是我的代码:
public class WebClientGetById extends AbstractVerticle {
private String output;
@Override
public void start() {
//DO NOTHING
}
public String getById(String myId) {
output = null;
vertx.executeBlocking(p -> {
WebClient client = WebClient.create(vertx);
client
.get(8666, "localhost", "/getById")
.putHeader("id", myId)
.send()
.onSuccess(getSuccesssHandler());
});
return output;//THIS LINE IS EXECUTED BEFORE ONSUCCESS
}
private Handler<HttpResponse<Buffer>> getSuccesssHandler() {
return new Handler<HttpResponse<Buffer>>() {
@Override
public void handle(HttpResponse<Buffer> response) {
if (response.statusCode() == HttpResponseStatus.OK.code()) {
output = response.bodyAsString();
} else {
output = "Got error from server " + response.statusCode();
}
}
};
}
}
这是创建网络客户端并尝试使用 CRUD-API 的代码:
public class MyClass {
public static void main(String[] args) {
WebClientGetById verticle = new WebClientGetById ();
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(verticle);
String result = verticle.getById("myId");
System.out.println(result);
}
}
从注释行可以看出,在 webclient send() 返回其值之前,执行到了 getById() 的最后一行。所以 null 总是由 getById 返回。
这个类似问题的一部分似乎建议使用 executeBlocking 来实现我的目标。
vertx WebClient.send()方法是阻塞还是非阻塞
但是我试过了,没有用。
那么,Vert.x 实现这个目标的正确方法是什么?
(一段工作代码优于通用的理论解释)
在 webclient send 返回它的值之前执行到 getById 函数的最后一行
这句话表明你不了解异步编程是如何工作的——在使用vertx之前你需要学习异步编程的基础知识。
executeBlocking 会有所帮助,但似乎没用。
执行阻塞是将阻塞调用卸载到非事件循环线程上——它不会突然改变异步代码的工作方式
return output;//这一行在onSuccess之前执行!!!
是的,这是预期的,异步代码是如何工作的
没有办法从 onSuccess 或回调中“返回”一个值——你必须通过 futures 和 handlers 组合工作
如果你想编写能够按顺序执行和运行的异步代码,你应该放弃 java,改用 kotlin 和协程 - 执行 await 调用会给你结果,然后你可以像普通代码一样从函数返回它
你可以通过我的udemy课程了解更多关于如何使用vertx和异步代码https://www.udemy.com/course/backend-development-with-vertx/learn/?referralCode=063C2D57CCB957C5088C
联系一对一会议executeBlocking
中的promise参数用于将结果异步传递给调用者,前提是:
这就是为什么我在以下代码中将返回类型从
string
更改为 Future<String>
的原因。这里的理念是,在异步编程模型中,你必须异步地做所有事情(你可能会争辩说你可以做一些事情来等待结果,但在大多数情况下这是反模式)。可以删除输出字段以简化代码(如果您不介意的话):
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.ext.web.client.WebClient;
public class WebClientGetById extends AbstractVerticle {
@Override
public void start() {
//DO NOTHING
}
public Future<String> getById(String myId) {
return vertx.executeBlocking(
promise -> {
WebClient client = WebClient.create(vertx);
client
.get(8666, "localhost", "/getById")
.putHeader("id", myId)
.send()
.compose(response -> {
if (response.statusCode() == HttpResponseStatus.OK.code()) {
promise.complete(response.bodyAsString());
} else {
promise.fail("Got error from server " + response.statusCode());
}
return Future.succeededFuture();
})
.onFailure(promise::fail);
});
}
}
至于如何优雅地处理
getById
的结果,这里是指南(这是对方法的直接调用,verticle也可以通过事件总线或作为HTTP端点暴露方法,这需要其他工作)
public void exampleClientCall() {
// as a client, you should determine how to consume the data fetched from getById
// eg, to display on terminal, or as response to a http request
// one thing keep in mind to do it gracefully:
// - always consider the successful and failed cases
getById("xxxx")
.onSuccess(ret -> {
// display on stdout or response to http request with ret
})
.onFailure(exception -> {
// please never forget error handling
// do some log here
// display on stdout or response to http request with a default value or proper exception message
});
}