/*
* Copyright (c) 2011-2013 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.vertx.test.core;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Verticle;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.WorkerExecutor;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.datagram.DatagramSocket;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.MessageConsumer;
import io.vertx.core.eventbus.ReplyFailure;
import io.vertx.core.file.FileSystem;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpConnection;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.metrics.MetricsOptions;
import io.vertx.core.net.NetSocket;
import io.vertx.core.spi.metrics.PoolMetrics;
import io.vertx.test.fakemetrics.EndpointMetric;
import io.vertx.test.fakemetrics.FakeDatagramSocketMetrics;
import io.vertx.test.fakemetrics.FakeEventBusMetrics;
import io.vertx.test.fakemetrics.FakeHttpClientMetrics;
import io.vertx.test.fakemetrics.FakeHttpServerMetrics;
import io.vertx.test.fakemetrics.FakeMetricsBase;
import io.vertx.test.fakemetrics.FakeMetricsFactory;
import io.vertx.test.fakemetrics.FakePoolMetrics;
import io.vertx.test.fakemetrics.FakeVertxMetrics;
import io.vertx.test.fakemetrics.HandlerMetric;
import io.vertx.test.fakemetrics.HttpClientMetric;
import io.vertx.test.fakemetrics.HttpServerMetric;
import io.vertx.test.fakemetrics.PacketMetric;
import io.vertx.test.fakemetrics.ReceivedMessage;
import io.vertx.test.fakemetrics.SentMessage;
import io.vertx.test.fakemetrics.SocketMetric;
import io.vertx.test.fakemetrics.WebSocketMetric;
import org.junit.Test;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import static org.hamcrest.core.Is.*;
/**
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
*/
public class MetricsTest extends VertxTestBase {
private static final String ADDRESS1 = "some-address1";
@Override
protected VertxOptions getOptions() {
VertxOptions options = super.getOptions();
options.setMetricsOptions(new MetricsOptions().setEnabled(true).setFactory(new FakeMetricsFactory()));
return options;
}
@Test
public void testSendMessage() {
testBroadcastMessage(vertx, new Vertx[]{vertx}, false, true, false);
}
@Test
public void testSendMessageInCluster() {
startNodes(2);
testBroadcastMessage(vertices[0], new Vertx[]{vertices[1]}, false, false, true);
}
@Test
public void testEventBusInitializedWithCluster() {
startNodes(1);
assertWaitUntil(() -> FakeVertxMetrics.eventBus.get() != null);
}
@Test
public void testEventBusInitializedLocal() {
assertWaitUntil(() -> FakeVertxMetrics.eventBus.get() != null);
}
@Test
public void testPublishMessageToSelf() {
testBroadcastMessage(vertx, new Vertx[]{vertx}, true, true, false);
}
@Test
public void testPublishMessageToRemote() {
startNodes(2);
testBroadcastMessage(vertices[0], new Vertx[]{vertices[1]}, true, false, true);
}
@Test
public void testPublishMessageToCluster() {
startNodes(2);
testBroadcastMessage(vertices[0], vertices, true, true, true);
}
private void testBroadcastMessage(Vertx from, Vertx[] to, boolean publish, boolean expectedLocal, boolean expectedRemote) {
FakeEventBusMetrics eventBusMetrics = FakeMetricsBase.getMetrics(from.eventBus());
AtomicInteger broadcastCount = new AtomicInteger();
AtomicInteger receiveCount = new AtomicInteger();
for (Vertx vertx : to) {
MessageConsumer<Object> consumer = vertx.eventBus().consumer(ADDRESS1);
consumer.completionHandler(done -> {
assertTrue(done.succeeded());
if (broadcastCount.incrementAndGet() == to.length) {
String msg = TestUtils.randomAlphaString(10);
if (publish) {
from.eventBus().publish(ADDRESS1, msg);
} else {
from.eventBus().send(ADDRESS1, msg);
}
}
});
consumer.handler(msg -> {
if (receiveCount.incrementAndGet() == to.length) {
assertEquals(Arrays.asList(new SentMessage(ADDRESS1, publish, expectedLocal, expectedRemote)), eventBusMetrics.getSentMessages());
testComplete();
}
});
}
await();
}
@Test
public void testReceiveSentMessageFromSelf() {
testReceiveMessageSent(vertx, vertx, true, 1);
}
@Test
public void testReceiveMessageSentFromRemote() {
startNodes(2);
testReceiveMessageSent(vertices[0], vertices[1], false, 1);
}
private void testReceiveMessageSent(Vertx from, Vertx to, boolean expectedLocal, int expectedHandlers) {
FakeEventBusMetrics eventBusMetrics = FakeMetricsBase.getMetrics(to.eventBus());
MessageConsumer<Object> consumer = to.eventBus().consumer(ADDRESS1);
consumer.completionHandler(done -> {
assertTrue(done.succeeded());
String msg = TestUtils.randomAlphaString(10);
from.eventBus().send(ADDRESS1, msg);
});
consumer.handler(msg -> {
assertEquals(Arrays.asList(new ReceivedMessage(ADDRESS1, false, expectedLocal, expectedHandlers)), eventBusMetrics.getReceivedMessages());
testComplete();
});
await();
}
@Test
public void testReceivePublishedMessageFromSelf() {
testReceiveMessagePublished(vertx, vertx, true, 3);
}
@Test
public void testReceiveMessagePublishedFromRemote() {
startNodes(2);
testReceiveMessagePublished(vertices[0], vertices[1], false, 3);
}
private void testReceiveMessagePublished(Vertx from, Vertx to, boolean expectedLocal, int expectedHandlers) {
FakeEventBusMetrics eventBusMetrics = FakeMetricsBase.getMetrics(to.eventBus());
AtomicInteger count = new AtomicInteger();
for (int i = 0; i < expectedHandlers; i++) {
MessageConsumer<Object> consumer = to.eventBus().consumer(ADDRESS1);
consumer.completionHandler(done -> {
assertTrue(done.succeeded());
if (count.incrementAndGet() == expectedHandlers) {
String msg = TestUtils.randomAlphaString(10);
from.eventBus().publish(ADDRESS1, msg);
}
});
int index = i;
consumer.handler(msg -> {
if (index == 0) {
assertEquals(Arrays.asList(new ReceivedMessage(ADDRESS1, true, expectedLocal, expectedHandlers)), eventBusMetrics.getReceivedMessages());
testComplete();
}
});
}
await();
}
@Test
public void testReplyMessageFromSelf() {
testReply(vertx, vertx, true, false);
}
@Test
public void testReplyMessageFromRemote() {
startNodes(2);
testReply(vertices[0], vertices[1], false, true);
}
private void testReply(Vertx from, Vertx to, boolean expectedLocal, boolean expectedRemote) {
FakeEventBusMetrics fromMetrics = FakeMetricsBase.getMetrics(from.eventBus());
FakeEventBusMetrics toMetrics = FakeMetricsBase.getMetrics(to.eventBus());
MessageConsumer<Object> consumer = to.eventBus().consumer(ADDRESS1);
consumer.completionHandler(done -> {
assertTrue(done.succeeded());
String msg = TestUtils.randomAlphaString(10);
from.eventBus().send(ADDRESS1, msg, reply -> {
assertEquals(1, fromMetrics.getReceivedMessages().size());
ReceivedMessage receivedMessage = fromMetrics.getReceivedMessages().get(0);
assertEquals(false, receivedMessage.publish);
assertEquals(expectedLocal, receivedMessage.local);
assertEquals(1, receivedMessage.handlers);
assertEquals(1, toMetrics.getSentMessages().size());
SentMessage sentMessage = toMetrics.getSentMessages().get(0);
assertEquals(false, sentMessage.publish);
assertEquals(expectedLocal, sentMessage.local);
assertEquals(expectedRemote, sentMessage.remote);
assertEquals(sentMessage.address, receivedMessage.address);
testComplete();
});
});
consumer.handler(msg -> {
toMetrics.getReceivedMessages().clear();
toMetrics.getSentMessages().clear();
msg.reply(TestUtils.randomAlphaString(10));
});
await();
}
@Test
public void testHandlerRegistration() throws Exception {
FakeEventBusMetrics metrics = FakeMetricsBase.getMetrics(vertx.eventBus());
MessageConsumer<Object> consumer = vertx.eventBus().consumer(ADDRESS1, msg -> {
});
CountDownLatch latch = new CountDownLatch(1);
consumer.completionHandler(ar -> {
assertTrue(ar.succeeded());
latch.countDown();
});
awaitLatch(latch);
assertEquals(1, metrics.getRegistrations().size());
HandlerMetric registration = metrics.getRegistrations().get(0);
assertEquals(ADDRESS1, registration.address);
assertEquals(null, registration.repliedAddress);
consumer.unregister(ar -> {
assertTrue(ar.succeeded());
assertEquals(0, metrics.getRegistrations().size());
testComplete();
});
await();
}
@Test
public void testHandlerProcessMessage() {
testHandlerProcessMessage(vertx, vertx, 1);
}
@Test
public void testHandlerProcessMessageFromRemote() {
startNodes(2);
testHandlerProcessMessage(vertices[0], vertices[1], 0);
}
private HandlerMetric assertRegistration(FakeEventBusMetrics metrics) {
Optional<HandlerMetric> registration = metrics.getRegistrations().stream().filter(reg -> reg.address.equals(ADDRESS1)).findFirst();
assertTrue(registration.isPresent());
return registration.get();
}
private void testHandlerProcessMessage(Vertx from, Vertx to, int expectedLocalCount) {
FakeEventBusMetrics metrics = FakeMetricsBase.getMetrics(to.eventBus());
CountDownLatch latch1 = new CountDownLatch(1);
CountDownLatch latch2 = new CountDownLatch(1);
to.runOnContext(v -> {
to.eventBus().consumer(ADDRESS1, msg -> {
HandlerMetric registration = assertRegistration(metrics);
assertEquals(ADDRESS1, registration.address);
assertEquals(null, registration.repliedAddress);
assertEquals(1, registration.scheduleCount.get());
assertEquals(expectedLocalCount, registration.localScheduleCount.get());
assertEquals(1, registration.beginCount.get());
assertEquals(0, registration.endCount.get());
assertEquals(0, registration.failureCount.get());
assertEquals(expectedLocalCount, registration.localBeginCount.get());
msg.reply("pong");
}).completionHandler(onSuccess(v2 -> {
to.runOnContext(v3 -> {
latch1.countDown();
try {
awaitLatch(latch2);
} catch (InterruptedException e) {
fail(e);
}
});
}));
});
try {
awaitLatch(latch1);
} catch (InterruptedException e) {
fail(e);
return;
}
HandlerMetric registration = assertRegistration(metrics);
assertEquals(ADDRESS1, registration.address);
assertEquals(null, registration.repliedAddress);
from.eventBus().send(ADDRESS1, "ping", reply -> {
assertEquals(1, registration.scheduleCount.get());
assertEquals(1, registration.beginCount.get());
// This might take a little time
assertWaitUntil(() -> 1 == registration.endCount.get());
assertEquals(0, registration.failureCount.get());
assertEquals(expectedLocalCount, registration.localBeginCount.get());
testComplete();
});
assertWaitUntil(() -> registration.scheduleCount.get() == 1);
assertEquals(0, registration.beginCount.get());
latch2.countDown();
await();
}
@Test
public void testHandlerProcessMessageFailure() throws Exception {
FakeEventBusMetrics metrics = FakeMetricsBase.getMetrics(vertx.eventBus());
MessageConsumer<Object> consumer = vertx.eventBus().consumer(ADDRESS1, msg -> {
assertEquals(1, metrics.getReceivedMessages().size());
HandlerMetric registration = metrics.getRegistrations().get(0);
assertEquals(1, registration.scheduleCount.get());
assertEquals(1, registration.beginCount.get());
assertEquals(0, registration.endCount.get());
assertEquals(0, registration.failureCount.get());
throw new RuntimeException();
});
CountDownLatch latch = new CountDownLatch(1);
consumer.completionHandler(ar -> {
assertTrue(ar.succeeded());
latch.countDown();
});
awaitLatch(latch);
vertx.eventBus().send(ADDRESS1, "ping");
assertEquals(1, metrics.getReceivedMessages().size());
HandlerMetric registration = metrics.getRegistrations().get(0);
long now = System.currentTimeMillis();
while (registration.failureCount.get() < 1 && (System.currentTimeMillis() - now) < 10 * 1000) {
Thread.sleep(10);
}
assertEquals(1, registration.scheduleCount.get());
assertEquals(1, registration.beginCount.get());
assertEquals(1, registration.endCount.get());
assertEquals(1, registration.failureCount.get());
}
@Test
public void testHandlerMetricReply() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
FakeEventBusMetrics metrics = FakeMetricsBase.getMetrics(vertx.eventBus());
vertx.eventBus().consumer(ADDRESS1, msg -> {
assertEquals(ADDRESS1, metrics.getRegistrations().get(0).address);
assertWaitUntil(() -> metrics.getRegistrations().size() == 2);
HandlerMetric registration = metrics.getRegistrations().get(1);
assertEquals(ADDRESS1, registration.repliedAddress);
assertEquals(0, registration.scheduleCount.get());
assertEquals(0, registration.beginCount.get());
assertEquals(0, registration.endCount.get());
assertEquals(0, registration.localBeginCount.get());
msg.reply("pong");
}).completionHandler(ar -> {
assertTrue(ar.succeeded());
latch.countDown();
});
awaitLatch(latch);
vertx.eventBus().send(ADDRESS1, "ping", reply -> {
assertEquals(ADDRESS1, metrics.getRegistrations().get(0).address);
HandlerMetric registration = metrics.getRegistrations().get(1);
assertEquals(ADDRESS1, registration.repliedAddress);
assertEquals(1, registration.scheduleCount.get());
assertEquals(1, registration.beginCount.get());
assertEquals(0, registration.endCount.get());
assertEquals(1, registration.localBeginCount.get());
vertx.runOnContext(v -> {
assertEquals(ADDRESS1, metrics.getRegistrations().get(0).address);
assertEquals(ADDRESS1, registration.repliedAddress);
assertEquals(1, registration.scheduleCount.get());
assertEquals(1, registration.beginCount.get());
assertEquals(1, registration.endCount.get());
assertEquals(1, registration.localBeginCount.get());
});
testComplete();
});
await();
}
@Test
public void testBytesCodec() throws Exception {
startNodes(2);
FakeEventBusMetrics fromMetrics = FakeMetricsBase.getMetrics(vertices[0].eventBus());
FakeEventBusMetrics toMetrics = FakeMetricsBase.getMetrics(vertices[1].eventBus());
vertices[1].eventBus().consumer(ADDRESS1, msg -> {
int encoded = fromMetrics.getEncodedBytes(ADDRESS1);
int decoded = toMetrics.getDecodedBytes(ADDRESS1);
assertTrue("Expected to have more " + encoded + " > 1000 encoded bytes", encoded > 1000);
assertTrue("Expected to have more " + decoded + " > 1000 decoded bytes", decoded > 1000);
testComplete();
}).completionHandler(ar -> {
assertTrue(ar.succeeded());
assertEquals(0, fromMetrics.getEncodedBytes(ADDRESS1));
assertEquals(0, toMetrics.getDecodedBytes(ADDRESS1));
vertices[0].eventBus().send(ADDRESS1, Buffer.buffer(new byte[1000]));
});
await();
}
@Test
public void testReplyFailureNoHandlers() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
EventBus eb = vertx.eventBus();
eb.send(ADDRESS1, "bar", new DeliveryOptions().setSendTimeout(10), ar -> {
assertTrue(ar.failed());
latch.countDown();
});
awaitLatch(latch);
FakeEventBusMetrics metrics = FakeMetricsBase.getMetrics(eb);
assertEquals(Collections.singletonList(ADDRESS1), metrics.getReplyFailureAddresses());
assertEquals(Collections.singletonList(ReplyFailure.NO_HANDLERS), metrics.getReplyFailures());
}
@Test
public void testReplyFailureTimeout1() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
EventBus eb = vertx.eventBus();
FakeEventBusMetrics metrics = FakeMetricsBase.getMetrics(eb);
eb.consumer(ADDRESS1, msg -> {
// Do not reply
});
eb.send(ADDRESS1, "bar", new DeliveryOptions().setSendTimeout(10), ar -> {
assertTrue(ar.failed());
latch.countDown();
});
awaitLatch(latch);
assertEquals(1, metrics.getReplyFailureAddresses().size());
assertEquals(Collections.singletonList(ReplyFailure.TIMEOUT), metrics.getReplyFailures());
}
@Test
public void testReplyFailureTimeout2() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
EventBus eb = vertx.eventBus();
eb.consumer(ADDRESS1, msg -> {
msg.reply("juu", new DeliveryOptions().setSendTimeout(10), ar -> {
assertTrue(ar.failed());
latch.countDown();
});
});
eb.send(ADDRESS1, "bar", ar -> {
// Do not reply
});
awaitLatch(latch);
FakeEventBusMetrics metrics = FakeMetricsBase.getMetrics(eb);
assertEquals(1, metrics.getReplyFailureAddresses().size());
assertEquals(Collections.singletonList(ReplyFailure.TIMEOUT), metrics.getReplyFailures());
}
@Test
public void testReplyFailureRecipientFailure() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
EventBus eb = vertx.eventBus();
FakeEventBusMetrics metrics = FakeMetricsBase.getMetrics(eb);
AtomicReference<String> replyAddress = new AtomicReference<>();
CountDownLatch regLatch = new CountDownLatch(1);
eb.consumer("foo", msg -> {
replyAddress.set(msg.replyAddress());
msg.fail(0, "whatever");
}).completionHandler(onSuccess(v -> {
regLatch.countDown();
}));
awaitLatch(regLatch);
eb.send("foo", "bar", new DeliveryOptions(), ar -> {
assertTrue(ar.failed());
latch.countDown();
});
awaitLatch(latch);
assertEquals(Collections.singletonList(replyAddress.get()), metrics.getReplyFailureAddresses());
assertEquals(Collections.singletonList(ReplyFailure.RECIPIENT_FAILURE), metrics.getReplyFailures());
}
@Test
public void testServerWebSocket() throws Exception {
HttpServer server = vertx.createHttpServer();
server.websocketHandler(ws -> {
FakeHttpServerMetrics metrics = FakeMetricsBase.getMetrics(server);
WebSocketMetric metric = metrics.getMetric(ws);
assertNotNull(metric);
assertNotNull(metric.soMetric);
ws.handler(buffer -> {
ws.close();
});
ws.closeHandler(closed -> {
assertNull(metrics.getMetric(ws));
testComplete();
});
});
server.listen(HttpTestBase.DEFAULT_HTTP_PORT, HttpTestBase.DEFAULT_HTTP_HOST, ar -> {
assertTrue(ar.succeeded());
HttpClient client = vertx.createHttpClient();
client.websocket(HttpTestBase.DEFAULT_HTTP_PORT, HttpTestBase.DEFAULT_HTTP_HOST, "/", ws -> {
ws.write(Buffer.buffer("wibble"));
});
});
await();
}
@Test
public void testServerWebSocketUpgrade() throws Exception {
HttpServer server = vertx.createHttpServer();
server.requestHandler(req -> {
FakeHttpServerMetrics metrics = FakeMetricsBase.getMetrics(server);
assertNotNull(metrics.getMetric(req));
ServerWebSocket ws = req.upgrade();
assertNull(metrics.getMetric(req));
WebSocketMetric metric = metrics.getMetric(ws);
assertNotNull(metric);
assertNotNull(metric.soMetric);
ws.handler(buffer -> {
ws.close();
});
ws.closeHandler(closed -> {
assertNull(metrics.getMetric(ws));
testComplete();
});
});
server.listen(HttpTestBase.DEFAULT_HTTP_PORT, HttpTestBase.DEFAULT_HTTP_HOST, ar -> {
assertTrue(ar.succeeded());
HttpClient client = vertx.createHttpClient();
client.websocket(HttpTestBase.DEFAULT_HTTP_PORT, HttpTestBase.DEFAULT_HTTP_HOST, "/", ws -> {
ws.write(Buffer.buffer("wibble"));
});
});
await();
}
@Test
public void testWebSocket() throws Exception {
HttpServer server = vertx.createHttpServer();
server.websocketHandler(ws -> {
ws.write(Buffer.buffer("wibble"));
});
server.listen(HttpTestBase.DEFAULT_HTTP_PORT, HttpTestBase.DEFAULT_HTTP_HOST, ar -> {
assertTrue(ar.succeeded());
HttpClient client = vertx.createHttpClient();
client.websocket(HttpTestBase.DEFAULT_HTTP_PORT, HttpTestBase.DEFAULT_HTTP_HOST, "/", ws -> {
FakeHttpClientMetrics metrics = FakeMetricsBase.getMetrics(client);
WebSocketMetric metric = metrics.getMetric(ws);
assertNotNull(metric);
assertNotNull(metric.soMetric);
ws.closeHandler(closed -> {
assertNull(metrics.getMetric(ws));
testComplete();
});
ws.handler(buffer -> {
ws.close();
});
});
});
await();
}
@Test
public void testHttpClientName() throws Exception {
HttpClient client1 = vertx.createHttpClient();
FakeHttpClientMetrics metrics1 = FakeMetricsBase.getMetrics(client1);
assertEquals("", metrics1.getName());
String name = TestUtils.randomAlphaString(10);
HttpClient client2 = vertx.createHttpClient(new HttpClientOptions().setMetricsName(name));
FakeHttpClientMetrics metrics2 = FakeMetricsBase.getMetrics(client2);
assertEquals(name, metrics2.getName());
}
@Test
public void testHttpClientMetricsQueueLength() throws Exception {
HttpServer server = vertx.createHttpServer();
List<Runnable> requests = Collections.synchronizedList(new ArrayList<>());
server.requestHandler(req -> {
requests.add(() -> {
vertx.runOnContext(v -> {
req.response().end();
});
});
});
CountDownLatch listenLatch = new CountDownLatch(1);
server.listen(8080, "localhost", onSuccess(s -> { listenLatch.countDown(); }));
awaitLatch(listenLatch);
HttpClient client = vertx.createHttpClient();
FakeHttpClientMetrics metrics = FakeHttpClientMetrics.getMetrics(client);
CountDownLatch responsesLatch = new CountDownLatch(5);
for (int i = 0;i < 5;i++) {
client.getNow(8080, "localhost", "/somepath", resp -> {
responsesLatch.countDown();
});
}
assertWaitUntil(() -> requests.size() == 5);
assertEquals(Collections.singleton("localhost:8080"), metrics.endpoints());
assertEquals(0, (int)metrics.queueSize("localhost:8080"));
assertEquals(5, (int)metrics.connectionCount("localhost:8080"));
for (int i = 0;i < 8;i++) {
client.getNow(8080, "localhost", "/somepath", resp -> {
});
}
assertEquals(Collections.singleton("localhost:8080"), metrics.endpoints());
assertEquals(8, (int)metrics.queueSize("localhost:8080"));
assertEquals(5, (int)metrics.connectionCount("localhost:8080"));
ArrayList<Runnable> copy = new ArrayList<>(requests);
requests.clear();
copy.forEach(Runnable::run);
awaitLatch(responsesLatch);
assertWaitUntil(() -> requests.size() == 5);
assertEquals(Collections.singleton("localhost:8080"), metrics.endpoints());
assertEquals(3, (int)metrics.queueSize("localhost:8080"));
assertEquals(5, (int)metrics.connectionCount("localhost:8080"));
copy = new ArrayList<>(requests);
requests.clear();
copy.forEach(Runnable::run);
assertWaitUntil(() -> requests.size() == 3);
assertEquals(Collections.singleton("localhost:8080"), metrics.endpoints());
assertEquals(0, (int)metrics.queueSize("localhost:8080"));
assertEquals(5, (int)metrics.connectionCount("localhost:8080"));
}
@Test
public void testHttpClientMetricsQueueClose() throws Exception {
HttpServer server = vertx.createHttpServer();
List<Runnable> requests = Collections.synchronizedList(new ArrayList<>());
server.requestHandler(req -> {
requests.add(() -> {
vertx.runOnContext(v -> {
req.connection().close();
});
});
});
CountDownLatch listenLatch = new CountDownLatch(1);
server.listen(8080, "localhost", onSuccess(s -> { listenLatch.countDown(); }));
awaitLatch(listenLatch);
HttpClient client = vertx.createHttpClient();
FakeHttpClientMetrics metrics = FakeHttpClientMetrics.getMetrics(client);
for (int i = 0;i < 5;i++) {
client.getNow(8080, "localhost", "/somepath", resp -> {
});
}
assertWaitUntil(() -> requests.size() == 5);
EndpointMetric endpoint = metrics.endpoint("localhost:8080");
assertEquals(5, endpoint.connectionCount.get());
ArrayList<Runnable> copy = new ArrayList<>(requests);
requests.clear();
copy.forEach(Runnable::run);
assertWaitUntil(() -> metrics.endpoints().isEmpty());
assertEquals(0, endpoint.connectionCount.get());
}
@Test
public void testHttpClientConnectionCloseAfterRequestEnd() throws Exception {
CountDownLatch started = new CountDownLatch(1);
HttpClient client = vertx.createHttpClient();
AtomicReference<EndpointMetric> endpointMetrics = new AtomicReference<>();
vertx.createHttpServer().requestHandler(req -> {
endpointMetrics.set(((FakeHttpClientMetrics)FakeHttpClientMetrics.getMetrics(client)).endpoint("localhost:8080"));
req.response().end();
}).listen(8080, "localhost", ar -> {
assertTrue(ar.succeeded());
started.countDown();
});
awaitLatch(started);
CountDownLatch closed = new CountDownLatch(1);
HttpClientRequest req = client.get(8080, "localhost", "/somepath");
req.handler(resp -> {
resp.endHandler(v1 -> {
HttpConnection conn = req.connection();
conn.closeHandler(v2 -> {
closed.countDown();
});
conn.close();
});
});
req.end();
awaitLatch(closed);
EndpointMetric val = endpointMetrics.get();
assertWaitUntil(() -> val.connectionCount.get() == 0);
assertEquals(0, val.queueSize.get());
assertEquals(0, val.requests.get());
}
@Test
public void testMulti() {
HttpServer s1 = vertx.createHttpServer();
s1.requestHandler(req -> {
});
s1.listen(8080, ar1 -> {
assertTrue(ar1.succeeded());
HttpServer s2 = vertx.createHttpServer();
s2.requestHandler(req -> {
req.response().end();
});
s2.listen(8080, ar2 -> {
assertTrue(ar2.succeeded());
FakeHttpServerMetrics metrics1 = FakeMetricsBase.getMetrics(ar1.result());
assertSame(ar1.result(), metrics1.server);
FakeHttpServerMetrics metrics2 = FakeMetricsBase.getMetrics(ar2.result());
assertSame(ar2.result(), metrics2.server);
testComplete();
});
});
await();
}
@Test
public void testHttpConnect1() throws Exception {
testHttpConnect("localhost", socketMetric -> assertEquals("localhost", socketMetric.remoteName));
}
@Test
public void testHttpConnect2() throws Exception {
testHttpConnect(InetAddress.getLocalHost().getHostAddress(), socketMetric -> assertEquals(socketMetric.remoteAddress.host(), socketMetric.remoteName));
}
private void testHttpConnect(String host, Consumer<SocketMetric> checker) {
AtomicReference<HttpClientMetric> clientMetric = new AtomicReference<>();
HttpServer server = vertx.createHttpServer();
server.requestHandler(req -> {
FakeHttpServerMetrics metrics = FakeMetricsBase.getMetrics(server);
HttpServerMetric serverMetric = metrics.getMetric(req);
assertNotNull(serverMetric);
req.response().setStatusCode(200);
req.response().setStatusMessage("Connection established");
req.response().end();
NetSocket so = req.netSocket();
so.handler(req.netSocket()::write);
so.closeHandler(v -> {
assertNull(metrics.getMetric(req));
assertFalse(serverMetric.socket.connected.get());
assertEquals(5, serverMetric.socket.bytesRead.get());
assertEquals(5, serverMetric.socket.bytesWritten.get());
assertEquals(serverMetric.socket.remoteAddress.host(), serverMetric.socket.remoteName);
assertFalse(clientMetric.get().socket.connected.get());
assertEquals(5, clientMetric.get().socket.bytesRead.get());
assertEquals(5, clientMetric.get().socket.bytesWritten.get());
checker.accept(clientMetric.get().socket);
testComplete();
});
}).listen(8080, ar1 -> {
assertTrue(ar1.succeeded());
HttpClient client = vertx.createHttpClient();
HttpClientRequest request = client.request(HttpMethod.CONNECT, 8080, host, "/");
FakeHttpClientMetrics metrics = FakeMetricsBase.getMetrics(client);
request.handler(resp -> {
assertEquals(200, resp.statusCode());
clientMetric.set(metrics.getMetric(request));
assertNotNull(clientMetric.get());
NetSocket socket = resp.netSocket();
socket.write(Buffer.buffer("hello"));
socket.handler(buf -> {
assertEquals("hello", buf.toString());
assertNull(metrics.getMetric(request));
socket.close();
});
}).end();
});
await();
}
@Test
public void testDatagram1() throws Exception {
testDatagram("127.0.0.1", packet -> {
assertEquals("127.0.0.1", packet.remoteAddress.host());
assertEquals(1234, packet.remoteAddress.port());
assertEquals(5, packet.numberOfBytes);
});
}
@Test
public void testDatagram2() throws Exception {
testDatagram("localhost", packet -> {
assertEquals("localhost", packet.remoteAddress.host());
assertEquals(1234, packet.remoteAddress.port());
assertEquals(5, packet.numberOfBytes);
});
}
private void testDatagram(String host, Consumer<PacketMetric> checker) throws Exception {
DatagramSocket peer1 = vertx.createDatagramSocket();
DatagramSocket peer2 = vertx.createDatagramSocket();
CountDownLatch latch = new CountDownLatch(1);
peer1.handler(packet -> {
FakeDatagramSocketMetrics peer1Metrics = FakeMetricsBase.getMetrics(peer1);
FakeDatagramSocketMetrics peer2Metrics = FakeMetricsBase.getMetrics(peer2);
assertEquals(host, peer1Metrics.getLocalName());
assertEquals("127.0.0.1", peer1Metrics.getLocalAddress().host());
assertNull(peer2Metrics.getLocalAddress());
assertEquals(1, peer1Metrics.getReads().size());
PacketMetric read = peer1Metrics.getReads().get(0);
assertEquals(5, read.numberOfBytes);
assertEquals(0, peer1Metrics.getWrites().size());
assertEquals(0, peer2Metrics.getReads().size());
assertEquals(1, peer2Metrics.getWrites().size());
checker.accept(peer2Metrics.getWrites().get(0));
testComplete();
});
peer1.listen(1234, host, ar -> {
assertTrue(ar.succeeded());
latch.countDown();
});
awaitLatch(latch);
peer2.send("hello", 1234, host, ar -> {
assertTrue(ar.succeeded());
});
await();
}
@Test
public void testThreadPoolMetricsWithExecuteBlocking() {
Map<String, PoolMetrics> all = FakePoolMetrics.getPoolMetrics();
FakePoolMetrics metrics = (FakePoolMetrics) all.get("vert.x-worker-thread");
assertThat(metrics.getPoolSize(), is(getOptions().getInternalBlockingPoolSize()));
assertThat(metrics.numberOfIdleThreads(), is(getOptions().getWorkerPoolSize()));
Handler<Future<Void>> job = getSomeDumbTask();
AtomicInteger counter = new AtomicInteger();
AtomicBoolean hadWaitingQueue = new AtomicBoolean();
AtomicBoolean hadIdle = new AtomicBoolean();
AtomicBoolean hadRunning = new AtomicBoolean();
for (int i = 0; i < 100; i++) {
vertx.executeBlocking(
job,
ar -> {
if (metrics.numberOfWaitingTasks() > 0) {
hadWaitingQueue.set(true);
}
if (metrics.numberOfIdleThreads() > 0) {
hadIdle.set(true);
}
if (metrics.numberOfRunningTasks() > 0) {
hadRunning.set(true);
}
if (counter.incrementAndGet() == 100) {
testComplete();
}
}
);
}
await();
assertEquals(metrics.numberOfSubmittedTask(), 100);
assertEquals(metrics.numberOfCompletedTasks(), 100);
assertTrue(hadIdle.get());
assertTrue(hadWaitingQueue.get());
assertTrue(hadRunning.get());
assertEquals(metrics.numberOfIdleThreads(), getOptions().getWorkerPoolSize());
assertEquals(metrics.numberOfRunningTasks(), 0);
assertEquals(metrics.numberOfWaitingTasks(), 0);
}
@Test
public void testThreadPoolMetricsWithInternalExecuteBlocking() {
// Internal blocking thread pool is used by blocking file system actions.
Map<String, PoolMetrics> all = FakePoolMetrics.getPoolMetrics();
FakePoolMetrics metrics = (FakePoolMetrics) all.get("vert.x-internal-blocking");
assertThat(metrics.getPoolSize(), is(getOptions().getInternalBlockingPoolSize()));
assertThat(metrics.numberOfIdleThreads(), is(getOptions().getInternalBlockingPoolSize()));
AtomicInteger counter = new AtomicInteger();
AtomicBoolean hadWaitingQueue = new AtomicBoolean();
AtomicBoolean hadIdle = new AtomicBoolean();
AtomicBoolean hadRunning = new AtomicBoolean();
FileSystem system = vertx.fileSystem();
for (int i = 0; i < 100; i++) {
vertx.executeBlocking(
fut -> {
system.readFile("afile.html", buffer -> {
fut.complete(null);
});
},
ar -> {
if (metrics.numberOfWaitingTasks() > 0) {
hadWaitingQueue.set(true);
}
if (metrics.numberOfIdleThreads() > 0) {
hadIdle.set(true);
}
if (metrics.numberOfRunningTasks() > 0) {
hadRunning.set(true);
}
if (counter.incrementAndGet() == 100) {
testComplete();
}
}
);
}
await();
assertEquals(metrics.numberOfSubmittedTask(), 100);
assertEquals(metrics.numberOfCompletedTasks(), 100);
assertTrue(hadIdle.get());
assertTrue(hadWaitingQueue.get());
assertTrue(hadRunning.get());
assertEquals(metrics.numberOfIdleThreads(), getOptions().getWorkerPoolSize());
assertEquals(metrics.numberOfRunningTasks(), 0);
assertEquals(metrics.numberOfWaitingTasks(), 0);
}
@Test
public void testThreadPoolMetricsWithWorkerVerticle() {
testWithWorkerVerticle(new DeploymentOptions().setWorker(true));
}
@Test
public void testThreadPoolMetricsWithWorkerVerticleAndMultiThread() {
testWithWorkerVerticle(new DeploymentOptions().setWorker(true).setMultiThreaded(true));
}
private void testWithWorkerVerticle(DeploymentOptions options) {
AtomicInteger counter = new AtomicInteger();
Map<String, PoolMetrics> all = FakePoolMetrics.getPoolMetrics();
FakePoolMetrics metrics = (FakePoolMetrics) all.get("vert.x-worker-thread");
assertThat(metrics.getPoolSize(), is(getOptions().getInternalBlockingPoolSize()));
assertThat(metrics.numberOfIdleThreads(), is(getOptions().getWorkerPoolSize()));
AtomicBoolean hadWaitingQueue = new AtomicBoolean();
AtomicBoolean hadIdle = new AtomicBoolean();
AtomicBoolean hadRunning = new AtomicBoolean();
int count = 100;
Verticle worker = new AbstractVerticle() {
@Override
public void start(Future<Void> done) throws Exception {
vertx.eventBus().localConsumer("message", d -> {
try {
Thread.sleep(10);
if (metrics.numberOfWaitingTasks() > 0) {
hadWaitingQueue.set(true);
}
if (metrics.numberOfIdleThreads() > 0) {
hadIdle.set(true);
}
if (metrics.numberOfRunningTasks() > 0) {
hadRunning.set(true);
}
if (counter.incrementAndGet() == count) {
testComplete();
}
} catch (InterruptedException e) {
Thread.currentThread().isInterrupted();
}
}
);
done.complete();
}
};
vertx.deployVerticle(worker, options, s -> {
for (int i = 0; i < count; i++) {
vertx.eventBus().send("message", i);
}
});
await();
waitUntil(() -> count + 1 == metrics.numberOfCompletedTasks());
// The verticle deployment is also executed on the worker thread pool
assertEquals(count + 1, metrics.numberOfSubmittedTask());
assertEquals(count + 1, metrics.numberOfCompletedTasks());
assertTrue("Had no idle threads", hadIdle.get());
assertTrue("Had no waiting tasks", hadWaitingQueue.get());
assertTrue("Had running tasks", hadRunning.get());
assertEquals(getOptions().getWorkerPoolSize(), metrics.numberOfIdleThreads());
assertEquals(0, metrics.numberOfRunningTasks());
assertEquals(0, metrics.numberOfWaitingTasks());
}
@Test
public void testThreadPoolMetricsWithNamedExecuteBlocking() {
vertx.close(); // Close the instance automatically created
vertx = Vertx.vertx(new VertxOptions().setMetricsOptions(new MetricsOptions().setEnabled(true).setFactory(new FakeMetricsFactory())));
WorkerExecutor workerExec = vertx.createSharedWorkerExecutor("my-pool", 10);
Map<String, PoolMetrics> all = FakePoolMetrics.getPoolMetrics();
FakePoolMetrics metrics = (FakePoolMetrics) all.get("my-pool");
assertThat(metrics.getPoolSize(), is(10));
assertThat(metrics.numberOfIdleThreads(), is(10));
Handler<Future<Void>> job = getSomeDumbTask();
AtomicInteger counter = new AtomicInteger();
AtomicBoolean hadWaitingQueue = new AtomicBoolean();
AtomicBoolean hadIdle = new AtomicBoolean();
AtomicBoolean hadRunning = new AtomicBoolean();
for (int i = 0; i < 100; i++) {
workerExec.executeBlocking(
job,
false,
ar -> {
if (metrics.numberOfWaitingTasks() > 0) {
hadWaitingQueue.set(true);
}
if (metrics.numberOfIdleThreads() > 0) {
hadIdle.set(true);
}
if (metrics.numberOfRunningTasks() > 0) {
hadRunning.set(true);
}
if (counter.incrementAndGet() == 100) {
testComplete();
}
});
}
await();
assertEquals(metrics.numberOfSubmittedTask(), 100);
assertEquals(metrics.numberOfCompletedTasks(), 100);
assertTrue(hadIdle.get());
assertTrue(hadWaitingQueue.get());
assertTrue(hadRunning.get());
assertEquals(metrics.numberOfIdleThreads(), 10);
assertEquals(metrics.numberOfRunningTasks(), 0);
assertEquals(metrics.numberOfWaitingTasks(), 0);
}
@Test
public void testWorkerPoolClose() {
WorkerExecutor ex1 = vertx.createSharedWorkerExecutor("ex1");
WorkerExecutor ex1_ = vertx.createSharedWorkerExecutor("ex1");
WorkerExecutor ex2 = vertx.createSharedWorkerExecutor("ex2");
Map<String, PoolMetrics> all = FakePoolMetrics.getPoolMetrics();
FakePoolMetrics metrics1 = (FakePoolMetrics) all.get("ex1");
FakePoolMetrics metrics2 = (FakePoolMetrics) all.get("ex2");
assertNotNull(metrics1);
assertNotNull(metrics2);
assertNotSame(metrics1, metrics2);
assertFalse(metrics1.isClosed());
assertFalse(metrics2.isClosed());
ex1_.close();
assertFalse(metrics1.isClosed());
assertFalse(metrics2.isClosed());
ex1.close();
assertTrue(metrics1.isClosed());
assertFalse(metrics2.isClosed());
ex2.close();
assertTrue(metrics1.isClosed());
assertTrue(metrics2.isClosed());
}
private Handler<Future<Void>> getSomeDumbTask() {
return (future) -> {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().isInterrupted();
}
future.complete(null);
};
}
}