/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hdfs.qjournal.client; import java.net.InetSocketAddress; import java.util.Map; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.HdfsConfiguration; import org.apache.hadoop.metrics2.annotation.Metric; import org.apache.hadoop.metrics2.annotation.Metrics; import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem; import org.apache.hadoop.metrics2.lib.MetricsRegistry; import org.apache.hadoop.metrics2.lib.MutableQuantiles; import com.google.common.collect.Maps; /** * The metrics for a journal from the writer's perspective. */ @Metrics(about="Journal client metrics", context="dfs") class IPCLoggerChannelMetrics { final MetricsRegistry registry = new MetricsRegistry("NameNode"); private volatile IPCLoggerChannel ch; private final MutableQuantiles[] writeEndToEndLatencyQuantiles; private final MutableQuantiles[] writeRpcLatencyQuantiles; /** * In the case of the NN transitioning between states, edit logs are closed * and reopened. Thus, the IPCLoggerChannel instance that writes to a * given JournalNode may change over the lifetime of the process. * However, metrics2 doesn't have a function to unregister a set of metrics * and fails if a new metrics class is registered with the same name * as the existing one. Hence, we have to maintain our own registry * ("multiton") here, so that we have exactly one metrics instance * per JournalNode, and switch out the pointer to the underlying * IPCLoggerChannel instance. */ private static final Map<String, IPCLoggerChannelMetrics> REGISTRY = Maps.newHashMap(); private IPCLoggerChannelMetrics(IPCLoggerChannel ch) { this.ch = ch; Configuration conf = new HdfsConfiguration(); int[] intervals = conf.getInts(DFSConfigKeys.DFS_METRICS_PERCENTILES_INTERVALS_KEY); if (intervals != null) { writeEndToEndLatencyQuantiles = new MutableQuantiles[intervals.length]; writeRpcLatencyQuantiles = new MutableQuantiles[intervals.length]; for (int i = 0; i < writeEndToEndLatencyQuantiles.length; i++) { int interval = intervals[i]; writeEndToEndLatencyQuantiles[i] = registry.newQuantiles( "writesE2E" + interval + "s", "End-to-end time for write operations", "ops", "LatencyMicros", interval); writeRpcLatencyQuantiles[i] = registry.newQuantiles( "writesRpc" + interval + "s", "RPC RTT for write operations", "ops", "LatencyMicros", interval); } } else { writeEndToEndLatencyQuantiles = null; writeRpcLatencyQuantiles = null; } } private void setChannel(IPCLoggerChannel ch) { assert ch.getRemoteAddress().equals(this.ch.getRemoteAddress()); this.ch = ch; } static IPCLoggerChannelMetrics create(IPCLoggerChannel ch) { String name = getName(ch); synchronized (REGISTRY) { IPCLoggerChannelMetrics m = REGISTRY.get(name); if (m != null) { m.setChannel(ch); } else { m = new IPCLoggerChannelMetrics(ch); DefaultMetricsSystem.instance().register(name, null, m); REGISTRY.put(name, m); } return m; } } private static String getName(IPCLoggerChannel ch) { InetSocketAddress addr = ch.getRemoteAddress(); String addrStr = addr.getAddress().getHostAddress(); // IPv6 addresses have colons, which aren't allowed as part of // MBean names. Replace with '.' addrStr = addrStr.replace(':', '.'); return "IPCLoggerChannel-" + addrStr + "-" + addr.getPort(); } @Metric("Is the remote logger out of sync with the quorum") public String isOutOfSync() { return Boolean.toString(ch.isOutOfSync()); } @Metric("The number of transactions the remote log is lagging behind the " + "quorum") public long getCurrentLagTxns() { return ch.getLagTxns(); } @Metric("The number of milliseconds the remote log is lagging behind the " + "quorum") public long getLagTimeMillis() { return ch.getLagTimeMillis(); } @Metric("The number of bytes of pending data to be sent to the remote node") public int getQueuedEditsSize() { return ch.getQueuedEditsSize(); } public void addWriteEndToEndLatency(long micros) { if (writeEndToEndLatencyQuantiles != null) { for (MutableQuantiles q : writeEndToEndLatencyQuantiles) { q.add(micros); } } } public void addWriteRpcLatency(long micros) { if (writeRpcLatencyQuantiles != null) { for (MutableQuantiles q : writeRpcLatencyQuantiles) { q.add(micros); } } } }