Skip to content

Commit 605d506

Browse files
author
Paultagoras
committed
Additional metrics
1 parent fc2105f commit 605d506

File tree

3 files changed

+134
-21
lines changed

3 files changed

+134
-21
lines changed

client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import com.clickhouse.client.api.data_formats.internal.SerializerUtils;
1414
import com.clickhouse.client.api.enums.ProxyType;
1515
import com.clickhouse.client.api.http.ClickHouseHttpProto;
16+
import io.micrometer.core.annotation.Timed;
1617
import org.apache.hc.client5.http.AuthenticationStrategy;
1718
import org.apache.hc.client5.http.ConnectTimeoutException;
1819
import org.apache.hc.client5.http.classic.ExecChain;
@@ -94,7 +95,9 @@
9495
import java.util.Objects;
9596
import java.util.Properties;
9697
import java.util.Set;
98+
import java.util.concurrent.ConcurrentLinkedQueue;
9799
import java.util.concurrent.TimeUnit;
100+
import java.util.concurrent.atomic.AtomicLong;
98101
import java.util.function.Function;
99102

100103
public class HttpAPIClientHelper {
@@ -112,6 +115,9 @@ public class HttpAPIClientHelper {
112115

113116
private final Set<ClientFaultCause> defaultRetryCauses;
114117

118+
private AtomicLong requestCount = new AtomicLong(0);
119+
private AtomicLong failureCount = new AtomicLong(0);
120+
115121
private String defaultUserAgent;
116122
private Object metricsRegistry;
117123
public HttpAPIClientHelper(Map<String, String> configuration, Object metricsRegistry, boolean initSslContext) {
@@ -229,22 +235,12 @@ private HttpClientConnectionManager poolConnectionManager(LayeredConnectionSocke
229235

230236

231237
int networkBufferSize = MapUtils.getInt(chConfiguration, "client_network_buffer_size");
232-
ManagedHttpClientConnectionFactory connectionFactory = new ManagedHttpClientConnectionFactory(
238+
MeteredManagedHttpClientConnectionFactory connectionFactory = new MeteredManagedHttpClientConnectionFactory(
233239
Http1Config.custom()
234240
.setBufferSize(networkBufferSize)
235241
.build(),
236242
CharCodingConfig.DEFAULT,
237-
DefaultHttpResponseParserFactory.INSTANCE) {
238-
@Override
239-
public ManagedHttpClientConnection createConnection(Socket socket) throws IOException {
240-
long startT = System.nanoTime();
241-
try {
242-
return super.createConnection(socket);
243-
} finally {
244-
LOG.info("connection created in " + (System.nanoTime() - startT) + "ns");
245-
}
246-
}
247-
};
243+
DefaultHttpResponseParserFactory.INSTANCE);
248244

249245
connMgrBuilder.setConnectionFactory(connectionFactory);
250246
connMgrBuilder.setSSLSocketFactory(sslConnectionSocketFactory);
@@ -258,6 +254,12 @@ public ManagedHttpClientConnection createConnection(Socket socket) throws IOExce
258254
Class<?> micrometerLoader = getClass().getClassLoader().loadClass("com.clickhouse.client.api.metrics.MicrometerLoader");
259255
Method applyMethod = micrometerLoader.getDeclaredMethod("applyPoolingMetricsBinder", Object.class, String.class, PoolingHttpClientConnectionManager.class);
260256
applyMethod.invoke(micrometerLoader, metricsRegistry, mGroupName, phccm);
257+
258+
applyMethod = micrometerLoader.getDeclaredMethod("applyConnectionMetricsBinder", Object.class, String.class, MeteredManagedHttpClientConnectionFactory.class);
259+
applyMethod.invoke(micrometerLoader, metricsRegistry, mGroupName, connectionFactory);
260+
261+
applyMethod = micrometerLoader.getDeclaredMethod("applyFailureRatioMetricsBinder", Object.class, String.class, HttpAPIClientHelper.class);
262+
applyMethod.invoke(micrometerLoader, metricsRegistry, mGroupName, this);
261263
} catch (Exception e) {
262264
LOG.error("Failed to register metrics", e);
263265
}
@@ -418,9 +420,7 @@ public ClassicHttpResponse executeRequest(ClickHouseNode server, Map<String, Obj
418420
HttpClientContext context = HttpClientContext.create();
419421

420422
try {
421-
{
422-
// increment request count
423-
}
423+
requestCount.incrementAndGet();
424424
ClassicHttpResponse httpResponse = httpClient.executeOpen(null, req, context);
425425
boolean serverCompression = MapUtils.getFlag(requestConfig, chConfiguration, ClientConfigProperties.COMPRESS_SERVER_RESPONSE.getKey());
426426
httpResponse.setEntity(wrapResponseEntity(httpResponse.getEntity(), httpResponse.getCode(), serverCompression, useHttpCompression));
@@ -446,9 +446,9 @@ public ClassicHttpResponse executeRequest(ClickHouseNode server, Map<String, Obj
446446
LOG.warn("Failed to connect to '{}': {}", server.getHost(), e.getMessage());
447447
throw new ClientException("Failed to connect", e);
448448
} catch (ConnectionRequestTimeoutException | ServerException | NoHttpResponseException | ClientException | SocketTimeoutException e) {
449-
if (e instanceof ConnectionRequestTimeoutException) {
450-
// add failed request because of connection request timeout
451-
req.getConfig().getConnectionRequestTimeout(); // record timeout value
449+
failureCount.incrementAndGet();
450+
if (e instanceof ConnectionRequestTimeoutException || e instanceof SocketTimeoutException) {
451+
LOG.warn("Request failed with timeout: {}", req.getConfig().getConnectionRequestTimeout()); // record timeout value
452452
}
453453
throw e;
454454
} catch (Exception e) {
@@ -784,6 +784,10 @@ public void close() {
784784
}
785785

786786

787+
public long getRequestRatio() {//Metrics
788+
return requestCount.get() == 0 ? 0 : Math.round(((float) failureCount.get() / requestCount.get()) * 100);
789+
}
790+
787791
/**
788792
* This factory is used only when no ssl connections are required (no https endpoints).
789793
* Internally http client would create factory and spend time if no supplied.
@@ -804,4 +808,34 @@ public Socket connectSocket(TimeValue connectTimeout, Socket socket, HttpHost ho
804808
return null;
805809
}
806810
}
811+
812+
public class MeteredManagedHttpClientConnectionFactory extends ManagedHttpClientConnectionFactory {
813+
public MeteredManagedHttpClientConnectionFactory(Http1Config http1Config, CharCodingConfig charCodingConfig, DefaultHttpResponseParserFactory defaultHttpResponseParserFactory) {
814+
super(http1Config, charCodingConfig, defaultHttpResponseParserFactory);
815+
}
816+
817+
ConcurrentLinkedQueue<Long> times = new ConcurrentLinkedQueue<>();
818+
819+
820+
@Override
821+
public ManagedHttpClientConnection createConnection(Socket socket) throws IOException {
822+
long startT = System.currentTimeMillis();
823+
try {
824+
return super.createConnection(socket);
825+
} finally {
826+
long endT = System.currentTimeMillis();
827+
times.add(endT - startT);
828+
}
829+
}
830+
831+
public long getTime() {
832+
int count = times.size();
833+
long runningAverage = 0;
834+
for (int i = 0; i < count; i++) {
835+
runningAverage += times.poll();
836+
}
837+
838+
return count > 0 ? runningAverage / count : 0;
839+
}
840+
}
807841
}

client-v2/src/main/java/com/clickhouse/client/api/metrics/MicrometerLoader.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package com.clickhouse.client.api.metrics;
22

33
import com.clickhouse.client.api.ClientMisconfigurationException;
4+
import com.clickhouse.client.api.internal.HttpAPIClientHelper;
5+
import io.micrometer.core.instrument.Counter;
6+
import io.micrometer.core.instrument.Gauge;
47
import io.micrometer.core.instrument.MeterRegistry;
58
import io.micrometer.core.instrument.binder.httpcomponents.hc5.PoolingHttpClientConnectionManagerMetricsBinder;
69
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
@@ -14,4 +17,26 @@ public static void applyPoolingMetricsBinder(Object registry, String metricsGrou
1417
throw new ClientMisconfigurationException("Unsupported registry type." + registry.getClass());
1518
}
1619
}
20+
21+
public static void applyConnectionMetricsBinder(Object registry, String metricsGroupName, HttpAPIClientHelper.MeteredManagedHttpClientConnectionFactory factory) {
22+
if (registry instanceof MeterRegistry) {
23+
Gauge.builder("httpcomponents.httpclient.connect.time", factory, HttpAPIClientHelper.MeteredManagedHttpClientConnectionFactory::getTime)
24+
.description("The running average connection creation time.")
25+
.tag("httpclient", metricsGroupName)
26+
.register((MeterRegistry) registry);
27+
} else {
28+
throw new ClientMisconfigurationException("Unsupported registry type." + registry.getClass());
29+
}
30+
}
31+
32+
public static void applyFailureRatioMetricsBinder(Object registry, String metricsGroupName, HttpAPIClientHelper httpAPIClientHelper) {
33+
if (registry instanceof MeterRegistry) {
34+
Gauge.builder("httpcomponents.httpclient.request.ratio", httpAPIClientHelper, HttpAPIClientHelper::getRequestRatio)
35+
.description("The ratio of total requests to failures via timeout.")
36+
.tag("httpclient", metricsGroupName)
37+
.register((MeterRegistry) registry);
38+
} else {
39+
throw new ClientMisconfigurationException("Unsupported registry type." + registry.getClass());
40+
}
41+
}
1742
}

client-v2/src/test/java/com/clickhouse/client/metrics/MetricsTest.java

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import org.testng.annotations.BeforeMethod;
1919
import org.testng.annotations.Test;
2020

21+
import java.time.temporal.ChronoUnit;
22+
2123
import static org.testng.Assert.assertEquals;
2224

2325
public class MetricsTest extends BaseIntegrationTest {
@@ -53,21 +55,73 @@ public void testRegisterMetrics() throws Exception {
5355
Gauge totalMax = meterRegistry.get("httpcomponents.httpclient.pool.total.max").gauge();
5456
Gauge available = meterRegistry.get("httpcomponents.httpclient.pool.total.connections").tags("state", "available").gauge();
5557
Gauge leased = meterRegistry.get("httpcomponents.httpclient.pool.total.connections").tags("state", "leased").gauge();
58+
Gauge times = meterRegistry.get("httpcomponents.httpclient.connect.time").gauge();
59+
Gauge ratio = meterRegistry.get("httpcomponents.httpclient.request.ratio").gauge();
5660

57-
System.out.println("totalMax:" + totalMax.value() + ", available: " + available.value() + ", leased: " + leased.value());
61+
System.out.println("totalMax:" + totalMax.value() +
62+
", available: " + available.value() +
63+
", leased: " + leased.value() +
64+
", times: " + times.value() +
65+
", ratio: " + ratio.value());
5866
Assert.assertEquals((int)totalMax.value(), Integer.parseInt(ClientConfigProperties.HTTP_MAX_OPEN_CONNECTIONS.getDefaultValue()));
5967
Assert.assertEquals((int)available.value(), 1);
6068
Assert.assertEquals((int)leased.value(), 0);
69+
assertEquals((int)ratio.value(), 0);
6170

6271
try (QueryResponse response = client.query("SELECT 1").get()) {
6372
Assert.assertEquals((int)available.value(), 0);
6473
Assert.assertEquals((int)leased.value(), 1);
74+
assertEquals(ratio.value(), 0);
6575
}
6676

6777
Assert.assertEquals((int)available.value(), 1);
6878
Assert.assertEquals((int)leased.value(), 0);
6979
}
70-
// currently there are only 5 metrics that are monitored by micrometer (out of the box)
71-
assertEquals(meterRegistry.getMeters().size(), 5);
80+
// currently there are only 7 metrics that are monitored by micrometer (out of the box)
81+
assertEquals(meterRegistry.getMeters().size(), 7);
82+
}
83+
84+
@Test(groups = { "integration" }, enabled = true)
85+
public void testConnectionTime() throws Exception {
86+
ClickHouseNode node = getServer(ClickHouseProtocol.HTTP);
87+
boolean isSecure = isCloud();
88+
89+
try (Client client = new Client.Builder()
90+
.addEndpoint(Protocol.HTTP, "192.168.1.1", node.getPort(), isSecure)
91+
.setUsername("default")
92+
.setPassword(ClickHouseServerForTest.getPassword())
93+
.setDefaultDatabase(ClickHouseServerForTest.getDatabase())
94+
.setConnectTimeout(5, ChronoUnit.SECONDS)
95+
.registerClientMetrics(meterRegistry, "pool-test")
96+
.build()) {
97+
98+
client.ping();
99+
Gauge times = meterRegistry.get("httpcomponents.httpclient.connect.time").gauge();
100+
101+
Assert.assertTrue(times.value() > 0);
102+
assertEquals(times.value(), 0);//Second time should be 0
103+
}
104+
}
105+
106+
@Test(groups = { "integration" }, enabled = true)
107+
public void testConnectionRatio() throws Exception {
108+
ClickHouseNode node = getServer(ClickHouseProtocol.HTTP);
109+
boolean isSecure = isCloud();
110+
111+
try (Client client = new Client.Builder()
112+
.addEndpoint(Protocol.HTTP, "192.168.1.1", node.getPort(), isSecure)
113+
.setUsername("default")
114+
.setPassword(ClickHouseServerForTest.getPassword())
115+
.setDefaultDatabase(ClickHouseServerForTest.getDatabase())
116+
.setConnectTimeout(5, ChronoUnit.SECONDS)
117+
.registerClientMetrics(meterRegistry, "pool-test")
118+
.build()) {
119+
120+
client.ping();
121+
Gauge ratio = meterRegistry.get("httpcomponents.httpclient.request.ratio").gauge();
122+
123+
System.out.println("Ratio: " + ratio.value());
124+
Assert.assertTrue(ratio.value() > 99.0);
125+
}
72126
}
73127
}

0 commit comments

Comments
 (0)