V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
v2defe
V2EX  ›  程序员

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

  •  
  •   v2defe · 2023-06-30 11:16:40 +08:00 · 1249 次点击
    这是一个创建于 504 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这是一个关于 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);
    
        }
    }
    
    6 条回复    2023-06-30 22:27:47 +08:00
    v2defe
        1
    v2defe  
    OP
       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
        2
    v2defe  
    OP
       2023-06-30 11:31:05 +08:00
    请问有办法处理第二个问题吗? gpt 的回复是错误的
    ovoo
        3
    ovoo  
       2023-06-30 19:40:12 +08:00   ❤️ 1
    ```
    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
        4
    v2defe  
    OP
       2023-06-30 21:59:27 +08:00 via Android
    @ovoo 原来如此,可以增加尾处理器来实现啊
    v2defe
        5
    v2defe  
    OP
       2023-06-30 22:14:05 +08:00 via Android
    @ovoo 老哥厉害,是可用的。看起来跟 gpt 给出的思路大致是一致的,需要通过 vertx 支持的方式去插入定制的处理器,来实现异步回调。
    后来我发现似乎在使用了反向代理处理器的情况下,不应该再去调用 next()传递到下个处理器了。因为反向代理处理器会异步执行请求并写入响应并结束响应,如果传递到下个处理器有可能会同时对响应进行操作,从而发生冲突。
    但是还是很感谢提供了处理的思路。
    另外第一段我的代码写错了,纠正一下应该是 router.route().handler(ProxyHandler.create(proxy))。给后面搜索的人做提醒。
    ovoo
        6
    ovoo  
       2023-06-30 22:27:47 +08:00
    @v2defe 是的,因为不清楚 OP 的真实意图,Issue1 又提到希望要方法执行完毕再继续 next() ,所以仅仅是分享异步的写法。重点在于理解 router 的链式模型。如果要继续操作反向代理返回的数据,可以使用拦截器实现,官方文档有 demo 。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   976 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 20:17 · PVG 04:17 · LAX 12:17 · JFK 15:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.