一个关于 NIO 的问题,你能帮我看看吗?

317 天前
 v2defe

这是一个关于 vertx web server 的简单 demo. 我设置了两个 handler, 一个用于打印一行文字, 另一个用来做一个反向代理,将请求转发到 baidu.com.

第一个代码块是一个正确的写法,但我在这个需求上,想要把第二个 handler 用代理的形式放到第一个 handler 里面,即合二为一. 这个处理我写在第二个代码块里, 但我有一些疑问, 请您帮我解答一下疑惑?

我有三个问题, 并且将这些问题放在了代码注释里,以 Issue 开头标注.

原始代码块

public class VertxRouteDemo {

    public static void main(String[] args) {
        Vertx vertx = Vertx.vertx();

        HttpServer server = vertx.createHttpServer();
        
        Router router = Router.router(vertx);

        router.route()
                // 第一个 handler
                .handler(event -> {
                    System.out.println("Just print to confirm executed");
                    event.next();
                });

        HttpClient httpClient = vertx.createHttpClient();
        HttpProxy proxy = HttpProxy.reverseProxy( httpClient).origin(80, "baidu.com");
        
        // 第二个 handler
        router.route().handler(proxy);
        
        server.requestHandler(router);

        server.listen(18081);

    }
}

修改后的代码块

public class VertxRouteDemo {

    public static void main(String[] args) {
        Vertx vertx = Vertx.vertx();

        HttpServer server = vertx.createHttpServer();
        
        Router router = Router.router(vertx);

        router.route()
                .handler(event -> {
                    System.out.println("Just print to confirm executed");
                    // 把它挪到这里,相当于对第二个 handler 做了代理,打印一行信息确认是否执行
                    HttpClient httpClient = vertx.createHttpClient();
                    HttpProxy proxy = HttpProxy.reverseProxy( httpClient).origin(80, "baidu.com");

                    // Issue 1: 这里是否会导致阻塞?
                    // 我猜测大概是不会导致阻塞的,即非阻塞,就是说这个方法立即返回。
                    // 在非阻塞的前提下引出第二个问题,我希望这个方法执行完成后再去调用下面的 next(),这段代码逻辑是不是有问题?
                    // 因为这个方法立即返回,那就会立即去执行下面的 next()方法了
                    // Issue 2: 如果这里是非阻塞方法,那我应该怎么改才能达到目的
                    proxy.handle(event);
                    
                    event.next();
                });
        server.requestHandler(router);

        server.listen(18081);

    }
}

第二个修改后的代码块

public class VertxRouteDemo {

    public static void main(String[] args) {
        Vertx vertx = Vertx.vertx();

        HttpServer server = vertx.createHttpServer();
        
        Router router = Router.router(vertx);

        router.route()
                .handler(event -> {
                    System.out.println("Just print to confirm executed");
                    // 把它挪到这里,相当于对第二个 handler 做了代理,打印一行信息确认是否执行
                    HttpClient httpClient = vertx.createHttpClient();
                    HttpProxy proxy = HttpProxy.reverseProxy( httpClient).origin(80, "baidu.com");

                    // 假设 handle(event)是阻塞的,所以使用了这种方法去使它不阻塞 eventloop 线程
                    // Issue 3: 这是写是对的吗?
                    vertx.executeBlocking(promise -> {
                        proxyHandler.handle(event);
                        promise.complete();
                    }, result -> {
                        if (result.succeeded()) {
                            // 阻塞操作完成后异步调用 event.next()
                            event.next();
                        } else {
                            // 处理阻塞操作失败的情况
                            event.fail(result.cause());
                        }
                    });
                    
                    event.next();
                });
        server.requestHandler(router);

        server.listen(18081);

    }
}
1089 次点击
所在节点    程序员
6 条回复
v2defe
317 天前
<details>

<summary>以下是 chatGPT3.5 的回复, 关于 Issue2, 它给出了一个不存在的方法来解决</summary>


首先,让我回答你的三个问题:

Issue 1: 这里是否会导致阻塞?
答:不会导致阻塞。在 Vert.x 中,大部分操作都是非阻塞的,包括创建 HttpClient 和配置反向代理。这些方法会立即返回,不会阻塞事件循环线程。

Issue 2: 如果这里是非阻塞方法,那我应该怎么改才能达到目的?
答:在这种情况下,你可以使用异步回调来实现在代理处理完成后再调用 event.next()。可以通过 proxy.handle()方法的回调函数来处理这个逻辑,而不是在调用 proxy.handle()之后立即调用 event.next()。这样,event.next()将在代理处理完成后被调用。


```java
router.route().handler(event -> {
System.out.println("Just print to confirm executed");
HttpClient httpClient = vertx.createHttpClient();
HttpProxy proxy = HttpProxy.reverseProxy( httpClient).origin(80, "baidu.com");

proxy.handle(event, proxyResult -> {
// 在代理处理完成后调用 event.next()
event.next();
});
});
```
Issue 3: 这是写是对的吗?
答:这个代码块中使用了 vertx.executeBlocking 方法来将阻塞操作封装为异步操作。这种方法在某些情况下是有效的,但要注意的是,如果你的目标是将阻塞操作转换为非阻塞操作,那么在 executeBlocking 中执行阻塞操作的目的就没有实现。

在你的代码中,如果 proxy.handle()方法是非阻塞的,那么使用 vertx.executeBlocking 没有必要。如果 proxy.handle()方法是阻塞的,那么你的代码是正确的,因为它将阻塞操作封装在一个异步操作中,并在操作完成后调用 event.next()。只要确保阻塞操作在异步操作中得到正确处理即可。

希望以上解答能帮助你理解和解决问题。如果还有其他疑问,请随时提出。

</details>
v2defe
317 天前
请问有办法处理第二个问题吗? gpt 的回复是错误的
ovoo
316 天前
```
public void start(Promise<Void> startPromise) throws Exception {
HttpServer server = vertx.createHttpServer();
Router router = Router.router(vertx);
router.route()
.handler(event -> {
System.out.println(1);
HttpClient httpClient = vertx.createHttpClient();
HttpProxy proxy = HttpProxy.reverseProxy( httpClient).origin(80, "baidu.com");
proxy.handle(event.request());
event.addEndHandler(h -> event.next());
});
router.route().handler(e -> System.out.println(3));
server.requestHandler(router).exceptionHandler(throwable -> System.out.println(throwable.getMessage()));
server.listen(18081);
}

```


以上代码可以实现代理,并按 route 添加顺序打印出 1 , 3
v2defe
316 天前
@ovoo 原来如此,可以增加尾处理器来实现啊
v2defe
316 天前
@ovoo 老哥厉害,是可用的。看起来跟 gpt 给出的思路大致是一致的,需要通过 vertx 支持的方式去插入定制的处理器,来实现异步回调。
后来我发现似乎在使用了反向代理处理器的情况下,不应该再去调用 next()传递到下个处理器了。因为反向代理处理器会异步执行请求并写入响应并结束响应,如果传递到下个处理器有可能会同时对响应进行操作,从而发生冲突。
但是还是很感谢提供了处理的思路。
另外第一段我的代码写错了,纠正一下应该是 router.route().handler(ProxyHandler.create(proxy))。给后面搜索的人做提醒。
ovoo
316 天前
@v2defe 是的,因为不清楚 OP 的真实意图,Issue1 又提到希望要方法执行完毕再继续 next() ,所以仅仅是分享异步的写法。重点在于理解 router 的链式模型。如果要继续操作反向代理返回的数据,可以使用拦截器实现,官方文档有 demo 。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/952951

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX