/** * 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.metrics.MetricsContext; import org.apache.hadoop.metrics.MetricsRecord; import org.apache.hadoop.metrics.MetricsUtil; import org.apache.hadoop.metrics.Updater; import org.apache.hadoop.metrics.util.MetricsBase; import org.apache.hadoop.metrics.util.MetricsIntValue; import org.apache.hadoop.metrics.util.MetricsLongValue; import org.apache.hadoop.metrics.util.MetricsRegistry; import org.apache.hadoop.metrics.util.MetricsTimeVaryingRate; import com.google.common.collect.Maps; /** * The metrics for a journal from the writer's perspective. */ class IPCLoggerChannelMetrics implements Updater { private volatile IPCLoggerChannel ch; private final MetricsRecord metricsRecord; final MetricsRegistry registry = new MetricsRegistry(); private MetricsTimeVaryingRate writeEndToEndLatency; private MetricsTimeVaryingRate writeRpcLatency; private MetricsLongValue currentQueuedEditsSizeBytes; private MetricsLongValue currentLagTransactions; private MetricsLongValue currentLagTimeMicros; private MetricsIntValue isOutOfSync; /** * 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, MetricsRecord metricRecords, String name) { this.ch = ch; this.metricsRecord = metricRecords; writeEndToEndLatency = new MetricsTimeVaryingRate("writeEndToEndLatency_" + name, registry); writeRpcLatency = new MetricsTimeVaryingRate("writeRpcLatency_" + name, registry); currentQueuedEditsSizeBytes = new MetricsLongValue( "currentQueuedEditsSizeBytes_" + name, registry); currentLagTransactions = new MetricsLongValue("currentLagTransactions_" + name, registry); currentLagTimeMicros = new MetricsLongValue("currentLagTimeMicros_" + name, registry); isOutOfSync = new MetricsIntValue("isOutOfSync_" + name, registry); } 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 { MetricsContext metricsContext = MetricsUtil.getContext("dfs"); MetricsRecord metricsRecord = MetricsUtil.createRecord(metricsContext, "loggerchannel"); metricsRecord.setTag("loggerchannel", name); m = new IPCLoggerChannelMetrics(ch, metricsRecord, name); metricsContext.registerUpdater(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 "qjmchannel-" + addrStr + "-" + addr.getPort(); } public void setOutOfSync(boolean value) { isOutOfSync.set(value ? 1 : 0); } public void setCurrentLagTimeMicros(long value) { currentLagTimeMicros.set(value); } public void setCurrentLagTransactions(long value) { currentLagTransactions.set(value); } public void setCurrentQueuedEditsSizeBytes(long value) { currentQueuedEditsSizeBytes.set(value); } public void addWriteEndToEndLatency(long micros) { writeEndToEndLatency.inc(micros); } public void addWriteRpcLatency(long micros) { writeRpcLatency.inc(micros); } @Override public void doUpdates(MetricsContext context) { synchronized (this) { for (MetricsBase m : registry.getMetricsList()) { m.pushMetric(metricsRecord); } } metricsRecord.update(); } }