// Copyright 2016 Twitter. All rights reserved. // // Licensed 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 com.twitter.heron.network; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import com.google.protobuf.Message; import com.twitter.heron.common.basics.Communicator; import com.twitter.heron.common.basics.NIOLooper; import com.twitter.heron.common.basics.SingletonRegistry; import com.twitter.heron.common.config.SystemConfig; import com.twitter.heron.common.network.HeronClient; import com.twitter.heron.common.network.HeronSocketOptions; import com.twitter.heron.common.network.StatusCode; import com.twitter.heron.metrics.GatewayMetrics; import com.twitter.heron.proto.system.Common; import com.twitter.heron.proto.system.Metrics; import com.twitter.heron.proto.system.PhysicalPlans; /** * MetricsClient implements SocketClient and communicate with Metrics Manager, it will: * 1. Check whether we got a PhysicalPlanHelper in singletonRegistry, and if we got one we will * send the register request; so it will not send registerRequest directly when it is connected * 2. Handle relative response for register request * 3. It will no need call the onIncomingMessage(), since it will not accept any message */ public class MetricsManagerClient extends HeronClient { private static final Logger LOG = Logger.getLogger(MetricsManagerClient.class.getName()); private final PhysicalPlans.Instance instance; private final List<Communicator<Metrics.MetricPublisherPublishMessage>> outMetricsQueues; private final SystemConfig systemConfig; private final GatewayMetrics gatewayMetrics; public MetricsManagerClient(NIOLooper s, String metricsHost, int metricsPort, PhysicalPlans.Instance instance, List<Communicator<Metrics.MetricPublisherPublishMessage>> outs, HeronSocketOptions options, GatewayMetrics gatewayMetrics) { super(s, metricsHost, metricsPort, options); this.instance = instance; this.outMetricsQueues = outs; this.systemConfig = (SystemConfig) SingletonRegistry.INSTANCE.getSingleton(SystemConfig.HERON_SYSTEM_CONFIG); this.gatewayMetrics = gatewayMetrics; addMetricsManagerClientTasksOnWakeUp(); } private void addMetricsManagerClientTasksOnWakeUp() { Runnable task = new Runnable() { @Override public void run() { sendMetricsMessageIfNeeded(); } }; getNIOLooper().addTasksOnWakeup(task); } private void sendMetricsMessageIfNeeded() { if (isConnected()) { // We consider # of MetricsMessage would be small // So we just write them out all in one time for (Communicator<Metrics.MetricPublisherPublishMessage> c : outMetricsQueues) { while (!c.isEmpty()) { Metrics.MetricPublisherPublishMessage m = c.poll(); gatewayMetrics.updateSentMetricsSize(m.getSerializedSize()); gatewayMetrics.updateSentMetrics(m.getMetricsCount(), m.getExceptionsCount()); sendMessage(m); } } } } // Send out all the data public void sendAllMessage() { if (!isConnected()) { return; } LOG.info("Flushing all pending data in MetricsManagerClient"); // Collect all tuples in queue for (Communicator<Metrics.MetricPublisherPublishMessage> c : outMetricsQueues) { int size = c.size(); for (int i = 0; i < size; i++) { Metrics.MetricPublisherPublishMessage m = c.poll(); sendMessage(m); } } } @Override public void onError() { LOG.severe("Disconnected from Metrics Manager."); // Dispatch to onConnect(...) onConnect(StatusCode.CONNECT_ERROR); } @Override public void onConnect(StatusCode status) { // We will not send registerRequest when we are onConnect // We will send when we receive the PhysicalPlan sent by slave if (status != StatusCode.OK) { LOG.log(Level.WARNING, "Cannot connect to the metrics port with status: {0}, Will Retry..", status); Runnable r = new Runnable() { public void run() { start(); } }; getNIOLooper().registerTimerEvent( systemConfig.getInstanceReconnectMetricsmgrInterval(), r); return; } LOG.info("Connected to Metrics Manager. Ready to send register request"); sendRegisterRequest(); } // Build register request and send to metrics mgr private void sendRegisterRequest() { String hostname; try { hostname = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { throw new RuntimeException("GetHostName failed"); } Metrics.MetricPublisher publisher = Metrics.MetricPublisher.newBuilder(). setHostname(hostname). setPort(instance.getInfo().getTaskId()). setComponentName(instance.getInfo().getComponentName()). setInstanceId(instance.getInstanceId()). setInstanceIndex(instance.getInfo().getComponentIndex()). build(); Metrics.MetricPublisherRegisterRequest request = Metrics.MetricPublisherRegisterRequest.newBuilder(). setPublisher(publisher). build(); // The timeout would be the reconnect-interval-seconds sendRequest(request, null, Metrics.MetricPublisherRegisterResponse.newBuilder(), systemConfig.getInstanceReconnectMetricsmgrInterval()); } @Override public void onResponse(StatusCode status, Object ctx, Message response) { if (status != StatusCode.OK) { //TODO:- is this a good thing? throw new RuntimeException("Response from Metrics Manager not ok"); } if (Metrics.MetricPublisherRegisterResponse.class.isInstance(response)) { handleRegisterResponse((Metrics.MetricPublisherRegisterResponse) response); } else { throw new RuntimeException("Unknown kind of response received from Metrics Manager"); } } @Override public void onIncomingMessage(Message message) { throw new RuntimeException("MetricsClient got an unknown message from Metrics Manager"); } @Override public void onClose() { LOG.info("MetricsManagerClient exits"); } private void handleRegisterResponse(Metrics.MetricPublisherRegisterResponse response) { if (response.getStatus().getStatus() != Common.StatusCode.OK) { throw new RuntimeException("Metrics Manager returned a not ok response for register"); } LOG.info("We registered ourselves to the Metrics Manager"); } }