瞭解Vert.x:事件迴圈
讓Vert.x框架實現高度可擴充套件和高效能的核心是事件迴圈,更具體地說是Multi-Reactor模式,以及它的訊息匯流排,在Vert.x中稱為EventBus。
在本文中,我想解決有關事件迴圈的誤解,例如:
“Vert.x有EventLoop,所以它是單執行緒的,只使用一個CPU”?
要麼
“Vert.x是多執行緒的,所以它必須為每個Verticle建立一個執行緒”?
Multi-Reactor
Event Loop是Reactor設計模式的一個實現。
它的目標是不斷檢查新事件,並在每次新事件發生時,快速將其傳送給知道如何處理它的人。
但是透過僅使用一個執行緒來消費所有事件,我們基本上沒有充分利用我們的硬體。例如,Node.js應用程式通常會生成多個程式來解決該問題。
在提供良好隔離的同時,程式也很昂貴。Vert.x使用多個執行緒,這在系統資源方面更便宜點。
為了理解Multi-Reactor在實踐中的工作原理,我們將透過簡單的呼叫來檢查執行緒的數量 Thread.activeCount(),雖然不準確,但這足以滿足我們的目的。
讓我們先看看我們在程式開頭有多少執行緒:
Before starting VertX -> 1 thread
現在我們將啟動Vert.x應用程式:
Vertx vertx = Vertx.vertx();
再次檢查執行緒數:
After starting VertX -> 3 threads
因此,啟動Vert.x會產生2個額外的執行緒。一個是執行應用程式,另一個是呼叫vertx-blocked-thread-checker
現在讓我們部署一千個Verticle,看看它如何影響我們的執行緒數。Verticle是輕量級的actor,通常在事件迴圈上執行。
final Map<String, AtomicInteger> threadCounts = new ConcurrentHashMap<>(); int verticles = 1000; final CountDownLatch latch = new CountDownLatch(verticles); for (int i = 0; i < verticles; i++) { vertx.deployVerticle(new MyVerticle(threadCounts), c -> latch.countDown()); } latch.await(); |
threadCounts現在不要理會,因為它將在後面解釋。
我們在這裡使用CountDownLatch,因為Verticle是非同步部署的,我們希望確保在檢查執行緒數時已經部署了所有例項。
After deploying 1000 verticles -> 19 threads
之前我們有3個執行緒,現在又增加了16個執行緒。它們都以形式命名vert.x-eventloop-thread-X。您可以啟動一萬個Verticle,並且不會影響事件迴圈執行緒的數量。
到目前為止,有兩個重要的要點:
- Vert.x不是單執行緒的
- 事件迴圈執行緒的最大數量取決於CPU的數量,而不是部署的Verticle數量
您可以在此處檢視預設執行緒數:
L38>https://github.com/eclipse/vert.x/blob/master/src/main/java/io/vertx/core/VertxOptions.javaL38
現在是時候看看我們的Verticle是什麼樣的,為什麼我們傳遞HashMap給它:
class MyVerticle extends AbstractVerticle { private final Map<String, AtomicInteger> threadCounts; MyVerticle(Map<String, AtomicInteger> threadCounts) { this.threadCounts = threadCounts; } @Override public void start() { threadCounts.computeIfAbsent(Thread.currentThread().getName(), t -> new AtomicInteger(0)).incrementAndGet(); } } |
因此,當每個Verticle啟動時,它會記錄已分配的執行緒。
此程式碼有助於我們瞭解如何在Verticle之間劃分執行緒:
vert.x-eventloop-thread-0=125
vert.x-eventloop-thread-1=125
vert.x-eventloop-thread-2=125
vert.x-eventloop-thread-3=125
vert.x-eventloop-thread-4=125
vert.x-eventloop-thread-5=125
vert.x-eventloop-thread-6=125
vert.x-eventloop-thread-7=125
如您所見,每個新Verticle以迴圈方式獲取一個執行緒。
檢視結果您可能想知道,為什麼我們部署了16個事件迴圈執行緒,但Verticle僅在前8箇中註冊。原因是我們非常積極地部署Verticle。在常規應用程式中,您可能不會這樣做。
所以,讓我們放鬆一下。我們將部署相同的千個Verticle,但這一次,一個接一個:
private void deployMyVerticle(final Vertx vertx, final Map<String, AtomicInteger> threadCounts, final AtomicInteger counter, final int verticles) { vertx.deployVerticle(new MyVerticle(threadCounts), c -> { if (counter.incrementAndGet() < verticles) { deployMyVerticle(vertx, threadCounts, counter, verticles); } }); } |
結果是我們使用的執行緒比以前少:
vert.x-eventloop-thread-0 = 250
vert.x-eventloop-thread-1 = 250
vert.x-eventloop-thread-2 = 250
vert.x-eventloop-thread-3 = 250
那是因為框架有足夠的時間來做出反應。
Worker Verticle
worker verticle用於執行長時間執行或阻塞任務。讓我們現在以類似的方式部署一千個worker Verticle,看看會發生什麼:
final CountDownLatch workersLatch = new CountDownLatch(verticles); final DeploymentOptions worker = new DeploymentOptions().setWorker(true); for (int i = 0; i < verticles; i++) { vertx.deployVerticle(new MyVerticle(threadCounts), worker, c -> workersLatch.countDown()); } workersLatch.await(); |
執行緒數:
After deploying 1000 worker verticles -> 27 threads
部署一千個worker Verticle增加了另外20個執行緒。
這是因為工作者Verticle使用一個單獨的執行緒池,預設情況下大小為20。
L43>https://github.com/eclipse/vert.x/blob/master/src/main/java/io/vertx/core/VertxOptions.javaL43
您可以透過呼叫VertxOptions的setWorkerPoolSize()on 來控制此池的大小,然後在Vert.x初始化時傳遞它們:
final VertxOptions options = new VertxOptions().setWorkerPoolSize(10); Vertx vertx = Vertx.vertx(options); |
請注意,與常規Verticle不同,worker Verticle不會線上程之間均勻分佈,因為它們用於不同的目的:
vert.x-worker-thread-0=126
vert.x-worker-thread-1=39
vert.x-worker-thread-2=94
vert.x-worker-thread-3=118
vert.x-worker-thread-4=89
vert.x-worker-thread-5=114
vert.x-worker-thread-6=222
vert.x-worker-thread-7=79
vert.x-worker-thread-8=67
vert.x-worker-thread-9=50
可以類似的方式控制事件迴圈池的大小:
final VertxOptions options = new VertxOptions().setEventLoopPoolSize(4); Vertx vertx = Vertx.vertx(options); |
結論
以下是幾個要點:
- Vert.x是多執行緒框架
- 它使用受控數量的執行緒
- 對於事件迴圈任務,預設情況下,執行緒池的大小是CPU計數的兩倍
- 對於worker任務,預設情況下執行緒池的大小為20
- 可以輕鬆調整兩個執行緒池的大小
相關文章
- JS事件迴圈,瞭解一下?JS事件
- 深入瞭解nodejs的事件迴圈機制NodeJS事件
- 事件迴圈詳解事件
- JS事件迴圈詳解JS事件
- 10分鐘瞭解JS堆、棧以及事件迴圈的概念JS事件
- nodejs事件和事件迴圈詳解NodeJS事件
- 事件迴圈事件
- 深入瞭解 JavaScript 中的 for 迴圈JavaScript
- JavaScript 事件迴圈詳解(翻譯)JavaScript事件
- JS事件迴圈JS事件
- libuv事件迴圈事件
- 事件迴圈(event loop)事件OOP
- 理解javascript事件迴圈JavaScript事件
- nodejs事件迴圈NodeJS事件
- 瞭解下C# 迴圈C#
- 深入瞭解Flutter的isolate(1) ---- 事件迴圈(event loop)及程式碼執行順序Flutter事件OOP
- JS事件迴圈Event LoopJS事件OOP
- Javascript 事件迴圈event loopJavaScript事件OOP
- Redis 中的事件迴圈Redis事件
- JavaScript事件迴圈機制JavaScript事件
- 聊聊Javascript的事件迴圈JavaScript事件
- JavaScript事件迴圈(Event Loop)JavaScript事件OOP
- Node中的事件迴圈事件
- JS 事件迴圈(Event Loop)JS事件OOP
- JS事件迴圈EventLoop初探JS事件OOP
- JavaScript 事件迴圈機制JavaScript事件
- JavaScript-事件迴圈-eventLoopJavaScript事件OOP
- 協程與事件迴圈事件
- nodejs事件和事件迴圈簡介NodeJS事件
- 瞭解巢狀迴圈聯接、合併聯接巢狀
- javascript事件迴圈機制EventLoopJavaScript事件OOP
- JS專題之事件迴圈JS事件
- javascript之事件迴圈機制JavaScript事件
- JS 總結之事件迴圈JS事件
- js--事件迴圈機制JS事件
- 剖析nodejs的事件迴圈NodeJS事件
- 事件迴圈(Event Loop)淺析事件OOP
- Nodejs事件迴圈小記NodeJS事件