【问题标题】:How vertx handlers work?vertx 处理程序如何工作?
【发布时间】:2018-04-10 06:04:54
【问题描述】:

上周我阅读了有关 vertx 的文档。我不明白 vertx 处理程序是如何工作的?例如

public class Client extends AbstractVerticle{

    @Override
    public void start() throws Exception {
       final HttpClient httpClient = this.vertx.createHttpClient();
       this.vertx.setPeriodic(1000, handler->{
           httpClient.getNow(8080, "localhost", "/", responseHandler -> {
                System.out.println("response");
            });
       });
    }

}

而服务器是:

public class JdbcVertx extends AbstractVerticle{

    @Override
    public void start() throws Exception {
        JDBCClient client = JDBCClient.createNonShared(this.vertx, new JsonObject()
                                .put("url", "jdbc:postgresql://localhost:5432/test")
                                .put("user", "user")
                                .put("password", "password")
                                .put("driver_class", "org.postgresql.Driver")
                                .put("max_pool_size", 30));
        this.vertx.createHttpServer()
                .requestHandler(r -> {
                    client.getConnection(handler -> {                     
                        final SQLConnection connection = handler.result();
                        connection.execute(execute(), hndlr -> {
                                connection.close(closehndlr -> {                                 
                                        r.response().putHeader("content-type", "text/html").end("Response");
                                });                                   
                        });
                    });
                }).listen(8080);
    }

    private String execute(){
            return "insert into rubish (name) values ('test')";      
    }
}

(PS 我知道我首先应该检查处理程序是否成功,然后采取一些措施,但我删除了这个检查以简化代码,如果在 30 秒内没有任何响应,我也会从官方文档中删除异常处理程序)

从上面的代码中,客户端每秒发送一次请求并且不等待响应,但它有一个处理程序,当响应到来时将执行。

'JdbcVertx' 在端口 8080 上侦听,获取请求,在睡眠状态下插入数据库,例如 3 秒(我将 1_000_000 行放入数据库并创建索引以减慢插入时间),然后发送响应,因此每个请求都是非阻塞。

据我所知,vertx 只有一个名为 EventLoop 的线程,来自 jdbcVertx 的事件循环 get reqests 但不会立即返回响应,而是放置了一个处理程序,该处理程序将在 db 插入成功时执行。事件循环如何知道 IO 操作何时完成。我认为它使用这样的东西

if(Thread.currentThread().getState != 'blocked' && sec != 30){
    this.object.getHandler().execute();
} else if(sec == 30){
 Thread.currentThread.inerrupt();
  } else{
    sec++;
  }

但是我们只有一个线程,当我们有阻塞调用时,它没有线程,只有处理程序。

问题是,事件循环如何知道阻塞操作何时结束以及该执行处理程序的时间

【问题讨论】:

  • 老实说,我不明白你的问题是什么......
  • 问题是,事件循环如何知道阻塞操作何时结束以及该执行处理程序的时间

标签: java asynchronous vert.x event-loop


【解决方案1】:

但是我们只有一个线程,当我们有阻塞调用时,它没有线程,只有处理程序。它是如何工作的,如果我们可以使用处理程序,为什么我们需要使用 Worker Verticle?

处理程序只是在收到事件总线消息或 http 调用时触发的操作。它们并非旨在为您处理可伸缩性。如果你只使用handlers并且你的actions开始需要很长时间或者如果你有任何请求数量的增加,你会阻塞你的verticle的eventloop并且会有很多Thread xxxx has been blockedwarn。

回答处理程序如何工作以及为什么事件循环不等待处理程序结束来启动另一个处理程序,根据这个:https://vertx.io/docs/vertx-core/java/#_reactor_and_multi_reactor

每个 Vertx 实例都维护多个事件循环,而不是单个事件循环。默认情况下,我们根据机器上可用内核的数量来选择数字,但这可以被覆盖。

这意味着单个 Vertx 进程可以在您的服务器上扩展,这与 Node.js 不同。

我们将此模式称为多反应器模式,以区别于单线程反应器模式。

但在我看来,这还不足以为您处理所有可伸缩性和线程阻塞问题,您也应该阅读以下内容:https://vertx.io/docs/vertx-core/java/#golden_rule

有很多方法可以设计垂直,但你必须尽可能保持非阻塞。在我看来,将 vert.x 与传统的阻塞方法(例如阻塞 restfull 端点)一起使用是不相关的。

我个人会按如下方式设计我的verticles:

  • verticle A : 暴露一个 restfull 端点并接受一个回调 url(无论是 GET/POST/PUT/PATCH/DELETE 操作)。 verticle 总是在没有结果的情况下立即响应202 Accepted,并在 eventbus 中向 verticle B 发送消息。

  • verticle B :获取消息,执行操作(最终与 eventbus 异步调用其他 verticles 并等待回复)并回复调用回调 url。

我会避免使用worker verticleexecuteBlocking 方法,甚至避免创建线程池。我将特权乘以我的 Verticle B 的实例(在单独的 pid 中),这些实例侦听相同的 eventbus 集群(最终是带有 http 反向代理的 Verticle A)。我们甚至可以想象根据实时请求的数量,拥有可变数量的 Verticle B 实例(在单独的 pid 中)。

P.S : 有时我会使用更强大的消息代理工具,例如 Apache Kafka,而不是原生事件总线(当我需要尊重某种消息,或者当我需要重播某些消息时)。

【讨论】:

  • 谢谢,但这与我关于处理程序的问题有何关系?
  • @AlmasAbdrazak 我认为仅使用 http 处理程序不足以处理可伸缩性和线程阻塞问题,仅此而已。我回答你关于why do we need worker verticle 的问题。我已经更新了我的答案以澄清一点;)
  • 这只是一个例子,我想表明事件循环不会等待每个请求完成,而是使用处理程序,它们是如何工作的,这是问题,而不是如何设计我的带有顶点的应用
  • 此外,使用事件总线的发送方法,您可以初始化响应操作的处理程序
  • @AlmasAbdrazak 我已经更新了我的答案,以提供更多关于 vert.x 多反应器模式的解释(你的问题不是很清楚,你似乎问为什么你应该做其他事情,比如使用 vert.x worker 而不是只使用 http 处理程序,我已经回答了这个问题)。总而言之,我真的认为您不需要仅仅依靠处理程序来处理可伸缩性和线程阻塞问题,正如您在第一条消息中所说的那样。这就是我回答的目的!我认为这是相关的;)
【解决方案2】:

回答问题:

事件循环如何知道阻塞操作何时结束以及何时执行处理程序?

根据非阻塞模型,事件循环调用

connection.execute( execute(), hndlr )

生成一个新线程,执行您的阻塞代码,并在它完成后(类似于Thread.join())在事件循环线程中调用hndlr 回调。因此,虽然可以执行阻塞代码,但主循环不会被阻塞。

【讨论】:

  • 谢谢,根据你的回答'spawns a new thread' ,这意味着所有阻塞操作都会产生一个新线程,等待操作完成,然后事件循环线程检查状态每次迭代后新产生的线程,对吗?
  • 大概是的。事件循环不关心它产生的线程,它只是从它们那里接收“完成信号”来执行处理程序。
  • 非常感谢,但我在官方文档中没有看到类似的东西,我想知道事件循环可以创建多少线程,这些线程是否来自池,我可以在哪里找到此类信息?
  • @AlmasAbdrazak 你可以阅读官方文档的 multireactor 部分(正如我之前所说);)vertx.io/docs/vertx-core/java/#_reactor_and_multi_reactor
  • @injecteer 我认为最好先介绍黄金法则,然后再介绍如何规避它(我说的是executeBlocking 方法,它对良好的非阻塞设计毫无用处)。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-16
  • 1970-01-01
相关资源
最近更新 更多