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

2023-06-30 11:16:40 +08:00
 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);

    }
}
1271 次点击
所在节点    程序员
6 条回复
v2defe
2023-06-30 11:19:24 +08:00
<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
2023-06-30 11:31:05 +08:00
请问有办法处理第二个问题吗? gpt 的回复是错误的
ovoo
2023-06-30 19:40:12 +08:00
```
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
2023-06-30 21:59:27 +08:00
@ovoo 原来如此,可以增加尾处理器来实现啊
v2defe
2023-06-30 22:14:05 +08:00
@ovoo 老哥厉害,是可用的。看起来跟 gpt 给出的思路大致是一致的,需要通过 vertx 支持的方式去插入定制的处理器,来实现异步回调。
后来我发现似乎在使用了反向代理处理器的情况下,不应该再去调用 next()传递到下个处理器了。因为反向代理处理器会异步执行请求并写入响应并结束响应,如果传递到下个处理器有可能会同时对响应进行操作,从而发生冲突。
但是还是很感谢提供了处理的思路。
另外第一段我的代码写错了,纠正一下应该是 router.route().handler(ProxyHandler.create(proxy))。给后面搜索的人做提醒。
ovoo
2023-06-30 22:27:47 +08:00
@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