2626import com .codahale .metrics .MetricRegistry ;
2727import com .codahale .metrics .RatioGauge ;
2828import com .codahale .metrics .Timer ;
29+ import java .util .ArrayList ;
30+ import java .util .List ;
2931import java .util .concurrent .ConcurrentHashMap ;
3032import java .util .concurrent .ConcurrentMap ;
3133import java .util .concurrent .ConcurrentSkipListMap ;
4749/**
4850 * This class is for maintaining the various connection statistics and publishing them through the
4951 * metrics interfaces. This class manages its own {@link MetricRegistry} and {@link JmxReporter} so
50- * as to not conflict with other uses of Yammer Metrics within the client application. Instantiating
51- * this class implicitly creates and "starts" instances of these classes; be sure to call
52- * {@link #shutdown()} to terminate the thread pools they allocate.
52+ * as to not conflict with other uses of Yammer Metrics within the client application. Calling
53+ * {@link #getMetricsConnection(String, Supplier, Supplier)} implicitly creates and "starts"
54+ * instances of these classes; be sure to call {@link #deleteMetricsConnection(String)} to terminate
55+ * the thread pools they allocate. The metrics reporter will be shutdown {@link #shutdown()} when
56+ * all connections within this metrics instances are closed.
5357 */
5458@ InterfaceAudience .Private
55- public class MetricsConnection implements StatisticTrackable {
59+ public final class MetricsConnection implements StatisticTrackable {
60+
61+ private static final ConcurrentMap <String , MetricsConnection > METRICS_INSTANCES =
62+ new ConcurrentHashMap <>();
63+
64+ static MetricsConnection getMetricsConnection (final String scope ,
65+ Supplier <ThreadPoolExecutor > batchPool , Supplier <ThreadPoolExecutor > metaPool ) {
66+ return METRICS_INSTANCES .compute (scope , (s , metricsConnection ) -> {
67+ if (metricsConnection == null ) {
68+ MetricsConnection newMetricsConn = new MetricsConnection (scope , batchPool , metaPool );
69+ newMetricsConn .incrConnectionCount ();
70+ return newMetricsConn ;
71+ } else {
72+ metricsConnection .addThreadPools (batchPool , metaPool );
73+ metricsConnection .incrConnectionCount ();
74+ return metricsConnection ;
75+ }
76+ });
77+ }
78+
79+ static void deleteMetricsConnection (final String scope ) {
80+ METRICS_INSTANCES .computeIfPresent (scope , (s , metricsConnection ) -> {
81+ metricsConnection .decrConnectionCount ();
82+ if (metricsConnection .getConnectionCount () == 0 ) {
83+ metricsConnection .shutdown ();
84+ return null ;
85+ }
86+ return metricsConnection ;
87+ });
88+ }
5689
5790 /** Set this key to {@code true} to enable metrics collection of client requests. */
5891 public static final String CLIENT_SIDE_METRICS_ENABLED_KEY = "hbase.client.metrics.enable" ;
@@ -231,7 +264,7 @@ public void updateDelayInterval(long interval) {
231264 }
232265 }
233266
234- protected ConcurrentHashMap <ServerName , ConcurrentMap <byte [], RegionStats >> serverStats =
267+ private ConcurrentHashMap <ServerName , ConcurrentMap <byte [], RegionStats >> serverStats =
235268 new ConcurrentHashMap <>();
236269
237270 public void updateServerStats (ServerName serverName , byte [] regionName , Object r ) {
@@ -272,7 +305,7 @@ private static interface NewMetric<T> {
272305
273306 private final MetricRegistry registry ;
274307 private final JmxReporter reporter ;
275- protected final String scope ;
308+ private final String scope ;
276309
277310 private final NewMetric <Timer > timerFactory = new NewMetric <Timer >() {
278311 @ Override
@@ -295,66 +328,93 @@ public Counter newMetric(Class<?> clazz, String name, String scope) {
295328 }
296329 };
297330
331+ // List of thread pool per connection of the metrics.
332+ private final List <Supplier <ThreadPoolExecutor >> batchPools = new ArrayList <>();
333+ private final List <Supplier <ThreadPoolExecutor >> metaPools = new ArrayList <>();
334+
298335 // static metrics
299336
300- protected final Counter metaCacheHits ;
301- protected final Counter metaCacheMisses ;
302- protected final CallTracker getTracker ;
303- protected final CallTracker scanTracker ;
304- protected final CallTracker appendTracker ;
305- protected final CallTracker deleteTracker ;
306- protected final CallTracker incrementTracker ;
307- protected final CallTracker putTracker ;
308- protected final CallTracker multiTracker ;
309- protected final RunnerStats runnerStats ;
310- protected final Counter metaCacheNumClearServer ;
311- protected final Counter metaCacheNumClearRegion ;
312- protected final Counter hedgedReadOps ;
313- protected final Counter hedgedReadWin ;
314- protected final Histogram concurrentCallsPerServerHist ;
315- protected final Histogram numActionsPerServerHist ;
316- protected final Counter nsLookups ;
317- protected final Counter nsLookupsFailed ;
318- protected final Timer overloadedBackoffTimer ;
337+ private final Counter connectionCount ;
338+ private final Counter metaCacheHits ;
339+ private final Counter metaCacheMisses ;
340+ private final CallTracker getTracker ;
341+ private final CallTracker scanTracker ;
342+ private final CallTracker appendTracker ;
343+ private final CallTracker deleteTracker ;
344+ private final CallTracker incrementTracker ;
345+ private final CallTracker putTracker ;
346+ private final CallTracker multiTracker ;
347+ private final RunnerStats runnerStats ;
348+ private final Counter metaCacheNumClearServer ;
349+ private final Counter metaCacheNumClearRegion ;
350+ private final Counter hedgedReadOps ;
351+ private final Counter hedgedReadWin ;
352+ private final Histogram concurrentCallsPerServerHist ;
353+ private final Histogram numActionsPerServerHist ;
354+ private final Counter nsLookups ;
355+ private final Counter nsLookupsFailed ;
356+ private final Timer overloadedBackoffTimer ;
319357
320358 // dynamic metrics
321359
322360 // These maps are used to cache references to the metric instances that are managed by the
323361 // registry. I don't think their use perfectly removes redundant allocations, but it's
324362 // a big improvement over calling registry.newMetric each time.
325- protected final ConcurrentMap <String , Timer > rpcTimers =
363+ private final ConcurrentMap <String , Timer > rpcTimers =
326364 new ConcurrentHashMap <>(CAPACITY , LOAD_FACTOR , CONCURRENCY_LEVEL );
327- protected final ConcurrentMap <String , Histogram > rpcHistograms = new ConcurrentHashMap <>(
365+ private final ConcurrentMap <String , Histogram > rpcHistograms = new ConcurrentHashMap <>(
328366 CAPACITY * 2 /* tracking both request and response sizes */ , LOAD_FACTOR , CONCURRENCY_LEVEL );
329367 private final ConcurrentMap <String , Counter > cacheDroppingExceptions =
330368 new ConcurrentHashMap <>(CAPACITY , LOAD_FACTOR , CONCURRENCY_LEVEL );
331- protected final ConcurrentMap <String , Counter > rpcCounters =
369+ private final ConcurrentMap <String , Counter > rpcCounters =
332370 new ConcurrentHashMap <>(CAPACITY , LOAD_FACTOR , CONCURRENCY_LEVEL );
333371
334- MetricsConnection (String scope , Supplier <ThreadPoolExecutor > batchPool ,
372+ private MetricsConnection (String scope , Supplier <ThreadPoolExecutor > batchPool ,
335373 Supplier <ThreadPoolExecutor > metaPool ) {
336374 this .scope = scope ;
375+ addThreadPools (batchPool , metaPool );
337376 this .registry = new MetricRegistry ();
338377 this .registry .register (getExecutorPoolName (), new RatioGauge () {
339378 @ Override
340379 protected Ratio getRatio () {
341- ThreadPoolExecutor pool = batchPool .get ();
342- if (pool == null ) {
343- return Ratio .of (0 , 0 );
380+ int numerator = 0 ;
381+ int denominator = 0 ;
382+ for (Supplier <ThreadPoolExecutor > poolSupplier : batchPools ) {
383+ ThreadPoolExecutor pool = poolSupplier .get ();
384+ if (pool != null ) {
385+ int activeCount = pool .getActiveCount ();
386+ int maxPoolSize = pool .getMaximumPoolSize ();
387+ /* The max thread usage ratio among batch pools of all connections */
388+ if (numerator == 0 || (numerator * maxPoolSize ) < (activeCount * denominator )) {
389+ numerator = activeCount ;
390+ denominator = maxPoolSize ;
391+ }
392+ }
344393 }
345- return Ratio .of (pool . getActiveCount (), pool . getMaximumPoolSize () );
394+ return Ratio .of (numerator , denominator );
346395 }
347396 });
348397 this .registry .register (getMetaPoolName (), new RatioGauge () {
349398 @ Override
350399 protected Ratio getRatio () {
351- ThreadPoolExecutor pool = metaPool .get ();
352- if (pool == null ) {
353- return Ratio .of (0 , 0 );
400+ int numerator = 0 ;
401+ int denominator = 0 ;
402+ for (Supplier <ThreadPoolExecutor > poolSupplier : metaPools ) {
403+ ThreadPoolExecutor pool = poolSupplier .get ();
404+ if (pool != null ) {
405+ int activeCount = pool .getActiveCount ();
406+ int maxPoolSize = pool .getMaximumPoolSize ();
407+ /* The max thread usage ratio among meta lookup pools of all connections */
408+ if (numerator == 0 || (numerator * maxPoolSize ) < (activeCount * denominator )) {
409+ numerator = activeCount ;
410+ denominator = maxPoolSize ;
411+ }
412+ }
354413 }
355- return Ratio .of (pool . getActiveCount (), pool . getMaximumPoolSize () );
414+ return Ratio .of (numerator , denominator );
356415 }
357416 });
417+ this .connectionCount = registry .counter (name (this .getClass (), "connectionCount" , scope ));
358418 this .metaCacheHits = registry .counter (name (this .getClass (), "metaCacheHits" , scope ));
359419 this .metaCacheMisses = registry .counter (name (this .getClass (), "metaCacheMisses" , scope ));
360420 this .metaCacheNumClearServer =
@@ -397,8 +457,84 @@ MetricRegistry getMetricRegistry() {
397457 return registry ;
398458 }
399459
400- public void shutdown () {
401- this .reporter .stop ();
460+ /** scope of the metrics object */
461+ public String getMetricScope () {
462+ return scope ;
463+ }
464+
465+ /** serverStats metric */
466+ public ConcurrentHashMap <ServerName , ConcurrentMap <byte [], RegionStats >> getServerStats () {
467+ return serverStats ;
468+ }
469+
470+ /** runnerStats metric */
471+ public RunnerStats getRunnerStats () {
472+ return runnerStats ;
473+ }
474+
475+ /** metaCacheNumClearServer metric */
476+ public Counter getMetaCacheNumClearServer () {
477+ return metaCacheNumClearServer ;
478+ }
479+
480+ /** metaCacheNumClearRegion metric */
481+ public Counter getMetaCacheNumClearRegion () {
482+ return metaCacheNumClearRegion ;
483+ }
484+
485+ /** hedgedReadOps metric */
486+ public Counter getHedgedReadOps () {
487+ return hedgedReadOps ;
488+ }
489+
490+ /** hedgedReadWin metric */
491+ public Counter getHedgedReadWin () {
492+ return hedgedReadWin ;
493+ }
494+
495+ /** numActionsPerServerHist metric */
496+ public Histogram getNumActionsPerServerHist () {
497+ return numActionsPerServerHist ;
498+ }
499+
500+ /** rpcCounters metric */
501+ public ConcurrentMap <String , Counter > getRpcCounters () {
502+ return rpcCounters ;
503+ }
504+
505+ /** getTracker metric */
506+ public CallTracker getGetTracker () {
507+ return getTracker ;
508+ }
509+
510+ /** scanTracker metric */
511+ public CallTracker getScanTracker () {
512+ return scanTracker ;
513+ }
514+
515+ /** multiTracker metric */
516+ public CallTracker getMultiTracker () {
517+ return multiTracker ;
518+ }
519+
520+ /** appendTracker metric */
521+ public CallTracker getAppendTracker () {
522+ return appendTracker ;
523+ }
524+
525+ /** deleteTracker metric */
526+ public CallTracker getDeleteTracker () {
527+ return deleteTracker ;
528+ }
529+
530+ /** incrementTracker metric */
531+ public CallTracker getIncrementTracker () {
532+ return incrementTracker ;
533+ }
534+
535+ /** putTracker metric */
536+ public CallTracker getPutTracker () {
537+ return putTracker ;
402538 }
403539
404540 /** Produce an instance of {@link CallStats} for clients to attach to RPCs. */
@@ -457,6 +593,28 @@ public void incrementServerOverloadedBackoffTime(long time, TimeUnit timeUnit) {
457593 overloadedBackoffTimer .update (time , timeUnit );
458594 }
459595
596+ /** Return the connection count of the metrics within a scope */
597+ public long getConnectionCount () {
598+ return connectionCount .getCount ();
599+ }
600+
601+ /** Increment the connection count of the metrics within a scope */
602+ private void incrConnectionCount () {
603+ connectionCount .inc ();
604+ }
605+
606+ /** Decrement the connection count of the metrics within a scope */
607+ private void decrConnectionCount () {
608+ connectionCount .dec ();
609+ }
610+
611+ /** Add thread pools of additional connections to the metrics */
612+ private void addThreadPools (Supplier <ThreadPoolExecutor > batchPool ,
613+ Supplier <ThreadPoolExecutor > metaPool ) {
614+ batchPools .add (batchPool );
615+ metaPools .add (metaPool );
616+ }
617+
460618 /**
461619 * Get a metric for {@code key} from {@code map}, or create it with {@code factory}.
462620 */
@@ -474,6 +632,10 @@ private void updateRpcGeneric(String methodName, CallStats stats) {
474632 .update (stats .getResponseSizeBytes ());
475633 }
476634
635+ private void shutdown () {
636+ this .reporter .stop ();
637+ }
638+
477639 /** Report RPC context to metrics system. */
478640 public void updateRpc (MethodDescriptor method , Message param , CallStats stats ) {
479641 int callsPerServer = stats .getConcurrentCallsPerServer ();
0 commit comments