【问题标题】:Vertx web-server uses only one event-loop thread while 16 are availableVertx 网络服务器仅使用一个事件循环线程,而 16 个可用
【发布时间】:2018-09-21 08:22:02
【问题描述】:

我使用 Vert.x v3.5.1。 有最简单的代码示例:

vertx.createHttpServer()
                    .requestHandler(anyRouter::accept)
                    .listen(8080);

在我的例子中,事件循环组的大小是 16,所以我希望我的请求会影响 16 个线程。服务器已成功启动,但它仅在一个线程中工作。 (我使用不同的 tcp-connections 发送请求,所以 keep-alive 不是这里的原因。)
HttpServerImpl 类包含 httpHandlerMgr,这个管理器处理一个事件循环池(命名为 availableWorkers)。在调试过程中,我看到这个池只包含 一个 工人。

使用 Verticle 模型并不能解决问题,仍然没有使用所有线程。

如果我在循环中多次创建服务器,它会有所帮助。结果,我有许多受影响的线程和一台共享服务器。但它看起来像解决方法。

问题是如何创建使用所有可用事件循环线程的 Web 服务器?

Verticle 下面的实现

因此,此实现使用了一半的可用线程(8 个线程)。但我希望它使用 16 :)

public static void main(String[] args) throws Exception {
        int eventLoopSize = 16;
        Vertx vertx = new VertxOptions().setEventLoopPoolSize(eventLoopSize);
       for (int i=0;i<eventLoopSize; i++) {
           vertx.deployVerticle(new MyServer(vertx), deploymentOptions);
       }
    }

public class MyServer implements Verticle {
final Vertx vertx;
public MyServer(Vertx vertx) {
   this.vertx = vertx;
}

@Override
void init(Vertx vertx, Context context) {
vertx.createHttpServer()
                    .requestHandler(anyRouter::accept)
                    .listen(8080);
}
}

【问题讨论】:

    标签: vert.x event-loop


    【解决方案1】:

    涉及一个线程,这正是事件循环模型。我推荐观看Philip Roberts: What the heck is the event loop anyway? | JSConf EU 2014。示例适用于浏览器,但概念与 Vert.x 或 Node 等服务器端事件循环系统相同。

    但是,使用 Vert.x,您通常会在 verticles 中组织代码(想想小型服务)。每个 Verticle 都分配了一个事件循环,但您可以部署多个实例。这就是您使用 CPU 的所有内核的方式。如果您是 Java 程序员并且第一次编写 Vert.x 应用程序,我建议您阅读此guide

    至于将您的 verticle 扩展到所有核心,问题是当您自己实例化 verticle 时,您实际上创建了单独的部署,并且无法保证使用不同的事件循环。

    正如Specifying number of verticle instances 中所解释的,您必须使用verticle 名称:

    使用 Verticle 名称部署 Verticle 时,您可以指定 您要部署的 Verticle 实例数:

    DeploymentOptions options = new DeploymentOptions().setInstances(16);
    vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);
    

    这对于跨多个内核轻松扩展很有用。例如 您可能需要部署一个 Web 服务器 Verticle 和多个核心 你的机器,所以你想部署多个实例来利用所有 核心。

    【讨论】:

    • 感谢您的回复!我希望我的服务器使用所有可用的内核,即所有事件循环线程。正如 Vertx 手册中所述,它似乎被称为“多反应器模式”:“每个 Vertx 实例维护多个事件循环,而不是单个事件循环。”我也尝试部署verticles,但它也没有按预期工作 - 由于事件循环的相应选择器依赖于未按预期增加的内部索引,因此并非所有线程都参与其中,结果我只得到了一半预期线程数。
    • 您能否使用 sn-ps 更新您的问题 1/ 代码部署具有多个实例的 verticle 2/ verticle 本身。我去看看
    • 当然。我更新了问题。但实际上这是 Verticle 模型的另一个故事。我最初的问题是——为什么网络服务器只使用一个事件循环线程而不是全部可用?
    • 不,这不是另一个故事。 HttpServer 事件始终由单个事件循环处理。但是当您部署多个 Verticle 实例(因此创建多个 HttpServer)时,Vert.x 会在不同服务器之间平衡请求。
    • 在 Verticles 的情况下,我有以下情况: - 我创建了事件循环池大小 = 16 的 Vertx 实例 - 我部署了 16 个 Verticle 但它们只影响了一半的可用线程 - 原因是平衡依赖于内部索引,并且每次迭代该索引都会增加两次。结果 - 只有 8 个受影响的线程。如果是没有 Verticles 的简单 Web 服务器,我只想在事件循环线程上扩展它。我可以在循环中创建 N 次服务器。这似乎很奇怪。
    【解决方案2】:

    据此:https://vertx.io/docs/vertx-core/java/#_reactor_and_multi_reactor

    即使一个 Vertx 实例维护多个事件循环,任何特定的处理程序都不会同时执行,并且在大多数情况下(除了工作程序 Verticle)将始终使用完全相同的事件循环调用。

    所以如果你想使用其他线程,你可以:

    如果您的 Verticle 公开了一个 http restfull API,我建议您使用经典的 http 反向代理并管理容器内的多个实例,或者如果您不能或端口使用不同的主机。并通过事件总线(或其他基于消息队列的系统)将操作委托给其他 Verticle。这是design 的示例。

    【讨论】:

    • 我只是部署了几个“普通”(不是工作人员)verticles,它们使用了几个事件循环。但不是所有的。但我不明白为什么“多反应模式”不适用于简单的网络服务器。为什么不默认使用所有的 netty 事件循环?
    【解决方案3】:

    经过一些尝试和讨论后我了解到(感谢 tsegismont),处理池中所有线程的唯一正确方法是:

    DeploymentOptions deploymentOptions = new DeploymentOptions()
            .setInstances(vertxOptions.getEventLoopPoolSize());
    
    vertx.deployVerticle(() -> new MyServerVerticle(), deploymentOptions);
    

    下面的实现有意外的行为:

    for (int i=0;i<vertxOptions.getEventLoopPoolSize();++i) {
       vertx.deployVerticle(new MyServerVerticle());
    }
    
    class MyServerVerticle implements Verticle {
     @Override
    public void init(Vertx vertx, Context context) {
        this.vertx=vertx;
    }
    
     @Override
     public void start(Future<Void> startFuture) throws Exception {
    vertx.createHttpServer()
                .requestHandler(anyRouter::accept)
                .listen(8080);
    }
    }
    

    并且没有正确的方法来创建不使用具有多个事件循环的 Verticle 模型的 Web 服务器。在这种情况下,我们只能循环创建服务器,但我不确定它是否总是正确的:

    class MyServerNotVerticle {
      public void start() {
       for (int i=0;i<vertxOptions.getEventLoopPoolSize();++i) {
           vertx.createHttpServer()
                    .requestHandler(anyRouter::accept)
                    .listen(8080);
        }
      }
    }
    

    【讨论】:

    • 在第三个示例中(直接调用createHttpServer()),请注意不要从现有的vertx线程调用。
    • Scrum 大师,您能解释一下您的评论吗?我看不出有任何理由阻止在一个线程中启动多个服务器。
    • 你可以在一个线程上启动它们,但如果你在一个vertx线程上启动它们(通过调用listen()),它们将共享相同的Context,因此使用相同的cpu线程。 (查看httpServer.listen()getOrCreateContext() 的来源)。只要从非顶点线程(例如main())调用第三个示例,它就可以工作
    猜你喜欢
    • 1970-01-01
    • 2017-09-17
    • 2010-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-16
    相关资源
    最近更新 更多