Repository: spark Updated Branches: refs/heads/master d513c99c1 -> 68df47aca
[SPARK-16405] Add metrics and source for external shuffle service ## What changes were proposed in this pull request? Since externalShuffleService is essential for spark, better monitoring for shuffle service is necessary. In order to do so, we added various metrics in shuffle service and imported into ExternalShuffleServiceSource for metric system. Metrics added in shuffle service: * registeredExecutorsSize * openBlockRequestLatencyMillis * registerExecutorRequestLatencyMillis * blockTransferRateBytes JIRA Issue: https://issues.apache.org/jira/browse/SPARK-16405 ## How was this patch tested? Some test cases are added to verify metrics as expected in metric system. Those unit test cases are shown in `ExternalShuffleBlockHandlerSuite ` Author: Yangyang Liu <[email protected]> Closes #14080 from lovexi/yangyang-metrics. Project: http://git-wip-us.apache.org/repos/asf/spark/repo Commit: http://git-wip-us.apache.org/repos/asf/spark/commit/68df47ac Tree: http://git-wip-us.apache.org/repos/asf/spark/tree/68df47ac Diff: http://git-wip-us.apache.org/repos/asf/spark/diff/68df47ac Branch: refs/heads/master Commit: 68df47aca55e99406b7b67ef3d4b1008abf1b8b6 Parents: d513c99 Author: Yangyang Liu <[email protected]> Authored: Tue Jul 12 10:13:58 2016 -0700 Committer: Reynold Xin <[email protected]> Committed: Tue Jul 12 10:13:58 2016 -0700 ---------------------------------------------------------------------- common/network-shuffle/pom.xml | 5 ++ .../shuffle/ExternalShuffleBlockHandler.java | 92 ++++++++++++++++---- .../shuffle/ExternalShuffleBlockResolver.java | 4 + .../ExternalShuffleBlockHandlerSuite.java | 21 +++++ .../spark/deploy/ExternalShuffleService.scala | 8 ++ .../deploy/ExternalShuffleServiceSource.scala | 37 ++++++++ 6 files changed, 150 insertions(+), 17 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/spark/blob/68df47ac/common/network-shuffle/pom.xml ---------------------------------------------------------------------- diff --git a/common/network-shuffle/pom.xml b/common/network-shuffle/pom.xml index 3c4f329..51c06b9 100644 --- a/common/network-shuffle/pom.xml +++ b/common/network-shuffle/pom.xml @@ -55,6 +55,11 @@ </dependency> <dependency> + <groupId>io.dropwizard.metrics</groupId> + <artifactId>metrics-core</artifactId> + </dependency> + + <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </dependency> http://git-wip-us.apache.org/repos/asf/spark/blob/68df47ac/common/network-shuffle/src/main/java/org/apache/spark/network/shuffle/ExternalShuffleBlockHandler.java ---------------------------------------------------------------------- diff --git a/common/network-shuffle/src/main/java/org/apache/spark/network/shuffle/ExternalShuffleBlockHandler.java b/common/network-shuffle/src/main/java/org/apache/spark/network/shuffle/ExternalShuffleBlockHandler.java index 22fd592..1cc0fb6 100644 --- a/common/network-shuffle/src/main/java/org/apache/spark/network/shuffle/ExternalShuffleBlockHandler.java +++ b/common/network-shuffle/src/main/java/org/apache/spark/network/shuffle/ExternalShuffleBlockHandler.java @@ -20,8 +20,15 @@ package org.apache.spark.network.shuffle; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Meter; +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricSet; +import com.codahale.metrics.Timer; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import org.slf4j.Logger; @@ -52,6 +59,7 @@ public class ExternalShuffleBlockHandler extends RpcHandler { @VisibleForTesting final ExternalShuffleBlockResolver blockManager; private final OneForOneStreamManager streamManager; + private final ShuffleMetrics metrics; public ExternalShuffleBlockHandler(TransportConf conf, File registeredExecutorFile) throws IOException { @@ -64,6 +72,7 @@ public class ExternalShuffleBlockHandler extends RpcHandler { public ExternalShuffleBlockHandler( OneForOneStreamManager streamManager, ExternalShuffleBlockResolver blockManager) { + this.metrics = new ShuffleMetrics(); this.streamManager = streamManager; this.blockManager = blockManager; } @@ -79,32 +88,50 @@ public class ExternalShuffleBlockHandler extends RpcHandler { TransportClient client, RpcResponseCallback callback) { if (msgObj instanceof OpenBlocks) { - OpenBlocks msg = (OpenBlocks) msgObj; - checkAuth(client, msg.appId); - - List<ManagedBuffer> blocks = Lists.newArrayList(); - for (String blockId : msg.blockIds) { - blocks.add(blockManager.getBlockData(msg.appId, msg.execId, blockId)); + final Timer.Context responseDelayContext = metrics.openBlockRequestLatencyMillis.time(); + try { + OpenBlocks msg = (OpenBlocks) msgObj; + checkAuth(client, msg.appId); + + List<ManagedBuffer> blocks = Lists.newArrayList(); + long totalBlockSize = 0; + for (String blockId : msg.blockIds) { + final ManagedBuffer block = blockManager.getBlockData(msg.appId, msg.execId, blockId); + totalBlockSize += block != null ? block.size() : 0; + blocks.add(block); + } + long streamId = streamManager.registerStream(client.getClientId(), blocks.iterator()); + logger.trace("Registered streamId {} with {} buffers for client {} from host {}", + streamId, + msg.blockIds.length, + client.getClientId(), + NettyUtils.getRemoteAddress(client.getChannel())); + callback.onSuccess(new StreamHandle(streamId, msg.blockIds.length).toByteBuffer()); + metrics.blockTransferRateBytes.mark(totalBlockSize); + } finally { + responseDelayContext.stop(); } - long streamId = streamManager.registerStream(client.getClientId(), blocks.iterator()); - logger.trace("Registered streamId {} with {} buffers for client {} from host {}", - streamId, - msg.blockIds.length, - client.getClientId(), - NettyUtils.getRemoteAddress(client.getChannel())); - callback.onSuccess(new StreamHandle(streamId, msg.blockIds.length).toByteBuffer()); } else if (msgObj instanceof RegisterExecutor) { - RegisterExecutor msg = (RegisterExecutor) msgObj; - checkAuth(client, msg.appId); - blockManager.registerExecutor(msg.appId, msg.execId, msg.executorInfo); - callback.onSuccess(ByteBuffer.wrap(new byte[0])); + final Timer.Context responseDelayContext = metrics.registerExecutorRequestLatencyMillis.time(); + try { + RegisterExecutor msg = (RegisterExecutor) msgObj; + checkAuth(client, msg.appId); + blockManager.registerExecutor(msg.appId, msg.execId, msg.executorInfo); + callback.onSuccess(ByteBuffer.wrap(new byte[0])); + } finally { + responseDelayContext.stop(); + } } else { throw new UnsupportedOperationException("Unexpected message: " + msgObj); } } + public MetricSet getAllMetrics() { + return metrics; + } + @Override public StreamManager getStreamManager() { return streamManager; @@ -143,4 +170,35 @@ public class ExternalShuffleBlockHandler extends RpcHandler { } } + /** + * A simple class to wrap all shuffle service wrapper metrics + */ + private class ShuffleMetrics implements MetricSet { + private final Map<String, Metric> allMetrics; + // Time latency for open block request in ms + private final Timer openBlockRequestLatencyMillis = new Timer(); + // Time latency for executor registration latency in ms + private final Timer registerExecutorRequestLatencyMillis = new Timer(); + // Block transfer rate in byte per second + private final Meter blockTransferRateBytes = new Meter(); + + private ShuffleMetrics() { + allMetrics = new HashMap<>(); + allMetrics.put("openBlockRequestLatencyMillis", openBlockRequestLatencyMillis); + allMetrics.put("registerExecutorRequestLatencyMillis", registerExecutorRequestLatencyMillis); + allMetrics.put("blockTransferRateBytes", blockTransferRateBytes); + allMetrics.put("registeredExecutorsSize", new Gauge<Integer>() { + @Override + public Integer getValue() { + return blockManager.getRegisteredExecutorsSize(); + } + }); + } + + @Override + public Map<String, Metric> getMetrics() { + return allMetrics; + } + } + } http://git-wip-us.apache.org/repos/asf/spark/blob/68df47ac/common/network-shuffle/src/main/java/org/apache/spark/network/shuffle/ExternalShuffleBlockResolver.java ---------------------------------------------------------------------- diff --git a/common/network-shuffle/src/main/java/org/apache/spark/network/shuffle/ExternalShuffleBlockResolver.java b/common/network-shuffle/src/main/java/org/apache/spark/network/shuffle/ExternalShuffleBlockResolver.java index 54e870a..7eefcca 100644 --- a/common/network-shuffle/src/main/java/org/apache/spark/network/shuffle/ExternalShuffleBlockResolver.java +++ b/common/network-shuffle/src/main/java/org/apache/spark/network/shuffle/ExternalShuffleBlockResolver.java @@ -146,6 +146,10 @@ public class ExternalShuffleBlockResolver { this.directoryCleaner = directoryCleaner; } + public int getRegisteredExecutorsSize() { + return executors.size(); + } + /** Registers a new Executor with all the configuration we need to find its shuffle files. */ public void registerExecutor( String appId, http://git-wip-us.apache.org/repos/asf/spark/blob/68df47ac/common/network-shuffle/src/test/java/org/apache/spark/network/shuffle/ExternalShuffleBlockHandlerSuite.java ---------------------------------------------------------------------- diff --git a/common/network-shuffle/src/test/java/org/apache/spark/network/shuffle/ExternalShuffleBlockHandlerSuite.java b/common/network-shuffle/src/test/java/org/apache/spark/network/shuffle/ExternalShuffleBlockHandlerSuite.java index c2e0b74..c036bc2 100644 --- a/common/network-shuffle/src/test/java/org/apache/spark/network/shuffle/ExternalShuffleBlockHandlerSuite.java +++ b/common/network-shuffle/src/test/java/org/apache/spark/network/shuffle/ExternalShuffleBlockHandlerSuite.java @@ -20,6 +20,8 @@ package org.apache.spark.network.shuffle; import java.nio.ByteBuffer; import java.util.Iterator; +import com.codahale.metrics.Meter; +import com.codahale.metrics.Timer; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -66,6 +68,12 @@ public class ExternalShuffleBlockHandlerSuite { verify(callback, times(1)).onSuccess(any(ByteBuffer.class)); verify(callback, never()).onFailure(any(Throwable.class)); + // Verify register executor request latency metrics + Timer registerExecutorRequestLatencyMillis = (Timer) ((ExternalShuffleBlockHandler) handler) + .getAllMetrics() + .getMetrics() + .get("registerExecutorRequestLatencyMillis"); + assertEquals(1, registerExecutorRequestLatencyMillis.getCount()); } @SuppressWarnings("unchecked") @@ -99,6 +107,19 @@ public class ExternalShuffleBlockHandlerSuite { assertEquals(block0Marker, buffers.next()); assertEquals(block1Marker, buffers.next()); assertFalse(buffers.hasNext()); + + // Verify open block request latency metrics + Timer openBlockRequestLatencyMillis = (Timer) ((ExternalShuffleBlockHandler) handler) + .getAllMetrics() + .getMetrics() + .get("openBlockRequestLatencyMillis"); + assertEquals(1, openBlockRequestLatencyMillis.getCount()); + // Verify block transfer metrics + Meter blockTransferRateBytes = (Meter) ((ExternalShuffleBlockHandler) handler) + .getAllMetrics() + .getMetrics() + .get("blockTransferRateBytes"); + assertEquals(10, blockTransferRateBytes.getCount()); } @Test http://git-wip-us.apache.org/repos/asf/spark/blob/68df47ac/core/src/main/scala/org/apache/spark/deploy/ExternalShuffleService.scala ---------------------------------------------------------------------- diff --git a/core/src/main/scala/org/apache/spark/deploy/ExternalShuffleService.scala b/core/src/main/scala/org/apache/spark/deploy/ExternalShuffleService.scala index adc0de1..37a19a4 100644 --- a/core/src/main/scala/org/apache/spark/deploy/ExternalShuffleService.scala +++ b/core/src/main/scala/org/apache/spark/deploy/ExternalShuffleService.scala @@ -23,6 +23,7 @@ import scala.collection.JavaConverters._ import org.apache.spark.{SecurityManager, SparkConf} import org.apache.spark.internal.Logging +import org.apache.spark.metrics.MetricsSystem import org.apache.spark.network.TransportContext import org.apache.spark.network.netty.SparkTransportConf import org.apache.spark.network.sasl.SaslServerBootstrap @@ -41,6 +42,8 @@ import org.apache.spark.util.{ShutdownHookManager, Utils} private[deploy] class ExternalShuffleService(sparkConf: SparkConf, securityManager: SecurityManager) extends Logging { + protected val masterMetricsSystem = + MetricsSystem.createMetricsSystem("shuffleService", sparkConf, securityManager) private val enabled = sparkConf.getBoolean("spark.shuffle.service.enabled", false) private val port = sparkConf.getInt("spark.shuffle.service.port", 7337) @@ -54,6 +57,8 @@ class ExternalShuffleService(sparkConf: SparkConf, securityManager: SecurityMana private var server: TransportServer = _ + private val shuffleServiceSource = new ExternalShuffleServiceSource(blockHandler) + /** Create a new shuffle block handler. Factored out for subclasses to override. */ protected def newShuffleBlockHandler(conf: TransportConf): ExternalShuffleBlockHandler = { new ExternalShuffleBlockHandler(conf, null) @@ -77,6 +82,9 @@ class ExternalShuffleService(sparkConf: SparkConf, securityManager: SecurityMana Nil } server = transportContext.createServer(port, bootstraps.asJava) + + masterMetricsSystem.registerSource(shuffleServiceSource) + masterMetricsSystem.start() } /** Clean up all shuffle files associated with an application that has exited. */ http://git-wip-us.apache.org/repos/asf/spark/blob/68df47ac/core/src/main/scala/org/apache/spark/deploy/ExternalShuffleServiceSource.scala ---------------------------------------------------------------------- diff --git a/core/src/main/scala/org/apache/spark/deploy/ExternalShuffleServiceSource.scala b/core/src/main/scala/org/apache/spark/deploy/ExternalShuffleServiceSource.scala new file mode 100644 index 0000000..e917679 --- /dev/null +++ b/core/src/main/scala/org/apache/spark/deploy/ExternalShuffleServiceSource.scala @@ -0,0 +1,37 @@ +/* + * 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.spark.deploy + +import javax.annotation.concurrent.ThreadSafe + +import com.codahale.metrics.{Gauge, MetricRegistry} + +import org.apache.spark.metrics.source.Source +import org.apache.spark.network.shuffle.ExternalShuffleBlockHandler + +/** + * Provides metrics source for external shuffle service + */ +@ThreadSafe +private class ExternalShuffleServiceSource +(blockHandler: ExternalShuffleBlockHandler) extends Source { + override val metricRegistry = new MetricRegistry() + override val sourceName = "shuffleService" + + metricRegistry.registerAll(blockHandler.getAllMetrics) +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
