org.apache.hadoop.yarn.sls.web.SLSWebApp.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.yarn.sls.web.SLSWebApp.java

Source

/**
 * 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.yarn.sls.web;

import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.hadoop.classification.InterfaceAudience.Private;
import org.apache.hadoop.classification.InterfaceStability.Unstable;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.event.SchedulerEventType;
import org.apache.hadoop.yarn.sls.SLSRunner;
import org.apache.hadoop.yarn.sls.scheduler.FairSchedulerMetrics;
import org.apache.hadoop.yarn.sls.scheduler.ResourceSchedulerWrapper;
import org.apache.hadoop.yarn.sls.scheduler.SchedulerMetrics;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.AbstractHandler;
import org.mortbay.jetty.handler.ResourceHandler;

import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;

@Private
@Unstable
public class SLSWebApp extends HttpServlet {
    private static final long serialVersionUID = 1905162041950251407L;
    private transient Server server;
    private transient ResourceSchedulerWrapper wrapper;
    private transient MetricRegistry metrics;
    private transient SchedulerMetrics schedulerMetrics;
    // metrics objects
    private transient Gauge jvmFreeMemoryGauge;
    private transient Gauge jvmMaxMemoryGauge;
    private transient Gauge jvmTotalMemoryGauge;
    private transient Gauge numRunningAppsGauge;
    private transient Gauge numRunningContainersGauge;
    private transient Gauge allocatedMemoryGauge;
    private transient Gauge allocatedVCoresGauge;
    private transient Gauge availableMemoryGauge;
    private transient Gauge availableVCoresGauge;
    private transient Histogram allocateTimecostHistogram;
    private transient Histogram handleTimecostHistogram;
    private transient Map<SchedulerEventType, Histogram> handleOperTimecostHistogramMap;
    private transient Map<String, Counter> queueAllocatedMemoryCounterMap;
    private transient Map<String, Counter> queueAllocatedVCoresCounterMap;
    private int port;
    private int ajaxUpdateTimeMS = 1000;
    // html page templates
    private String simulateInfoTemplate;
    private String simulateTemplate;
    private String trackTemplate;

    {
        // load templates
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        try {
            simulateInfoTemplate = FileUtils
                    .readFileToString(new File(cl.getResource("simulate.info.html.template").getFile()));
            simulateTemplate = FileUtils
                    .readFileToString(new File(cl.getResource("simulate.html.template").getFile()));
            trackTemplate = FileUtils.readFileToString(new File(cl.getResource("track.html.template").getFile()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        handleOperTimecostHistogramMap = new HashMap<>();
        queueAllocatedMemoryCounterMap = new HashMap<>();
        queueAllocatedVCoresCounterMap = new HashMap<>();
    }

    public SLSWebApp(ResourceSchedulerWrapper wrapper, int metricsAddressPort) {
        this.wrapper = wrapper;
        metrics = wrapper.getMetrics();
        handleOperTimecostHistogramMap = new HashMap<SchedulerEventType, Histogram>();
        queueAllocatedMemoryCounterMap = new HashMap<String, Counter>();
        queueAllocatedVCoresCounterMap = new HashMap<String, Counter>();
        schedulerMetrics = wrapper.getSchedulerMetrics();
        port = metricsAddressPort;
    }

    public void start() throws Exception {
        // static files
        final ResourceHandler staticHandler = new ResourceHandler();
        staticHandler.setResourceBase("html");

        Handler handler = new AbstractHandler() {
            @Override
            public void handle(String target, HttpServletRequest request, HttpServletResponse response,
                    int dispatch) {
                try {
                    // timeunit
                    int timeunit = 1000; // second, divide millionsecond / 1000
                    String timeunitLabel = "second";
                    if (request.getParameter("u") != null && request.getParameter("u").equalsIgnoreCase("m")) {
                        timeunit = 1000 * 60;
                        timeunitLabel = "minute";
                    }

                    // http request
                    if (target.equals("/")) {
                        printPageIndex(request, response);
                    } else if (target.equals("/simulate")) {
                        printPageSimulate(request, response, timeunit, timeunitLabel);
                    } else if (target.equals("/track")) {
                        printPageTrack(request, response, timeunit, timeunitLabel);
                    } else
                    // js/css request
                    if (target.startsWith("/js") || target.startsWith("/css")) {
                        response.setCharacterEncoding("utf-8");
                        staticHandler.handle(target, request, response, dispatch);
                    } else
                    // json request
                    if (target.equals("/simulateMetrics")) {
                        printJsonMetrics(request, response);
                    } else if (target.equals("/trackMetrics")) {
                        printJsonTrack(request, response);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };

        server = new Server(port);
        server.setHandler(handler);

        server.start();
    }

    public void stop() throws Exception {
        if (server != null) {
            server.stop();
        }
    }

    /**
     * index html page, show simulation info
     * path ""
     * @param request http request
     * @param response http response
     * @throws java.io.IOException
     */
    private void printPageIndex(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        response.setStatus(HttpServletResponse.SC_OK);

        String simulateInfo;
        if (SLSRunner.simulateInfoMap.isEmpty()) {
            String empty = "<tr><td colspan='2' align='center'>" + "No information available</td></tr>";
            simulateInfo = MessageFormat.format(simulateInfoTemplate, empty);
        } else {
            StringBuilder info = new StringBuilder();
            for (Map.Entry<String, Object> entry : SLSRunner.simulateInfoMap.entrySet()) {
                info.append("<tr>");
                info.append("<td class='td1'>").append(entry.getKey()).append("</td>");
                info.append("<td class='td2'>").append(entry.getValue()).append("</td>");
                info.append("</tr>");
            }
            simulateInfo = MessageFormat.format(simulateInfoTemplate, info.toString());
        }
        response.getWriter().println(simulateInfo);

        ((Request) request).setHandled(true);
    }

    /**
     * simulate html page, show several real-runtime chart
     * path "/simulate"
     * use d3.js
     * @param request http request
     * @param response http response
     * @throws java.io.IOException
     */
    private void printPageSimulate(HttpServletRequest request, HttpServletResponse response, int timeunit,
            String timeunitLabel) throws IOException {
        response.setContentType("text/html");
        response.setStatus(HttpServletResponse.SC_OK);

        // queues {0}
        Set<String> queues = wrapper.getQueueSet();
        StringBuilder queueInfo = new StringBuilder();

        int i = 0;
        for (String queue : queues) {
            queueInfo.append("legends[4][").append(i).append("] = 'queue.").append(queue)
                    .append(".allocated.memory';");
            queueInfo.append("legends[5][").append(i).append("] = 'queue.").append(queue)
                    .append(".allocated.vcores';");
            i++;
        }

        // time unit label {1}
        // time unit {2}
        // ajax update time interval {3}
        String simulateInfo = MessageFormat.format(simulateTemplate, queueInfo.toString(), timeunitLabel,
                "" + timeunit, "" + ajaxUpdateTimeMS);
        response.getWriter().println(simulateInfo);

        ((Request) request).setHandled(true);
    }

    /**
     * html page for tracking one queue or job
     * use d3.js
     * @param request http request
     * @param response http response
     * @throws java.io.IOException
     */
    private void printPageTrack(HttpServletRequest request, HttpServletResponse response, int timeunit,
            String timeunitLabel) throws IOException {
        response.setContentType("text/html");
        response.setStatus(HttpServletResponse.SC_OK);

        // tracked queues {0}
        StringBuilder trackedQueueInfo = new StringBuilder();
        Set<String> trackedQueues = wrapper.getQueueSet();
        for (String queue : trackedQueues) {
            trackedQueueInfo.append("<option value='Queue ").append(queue).append("'>").append(queue)
                    .append("</option>");
        }

        // tracked apps {1}
        StringBuilder trackedAppInfo = new StringBuilder();
        Set<String> trackedApps = wrapper.getTrackedAppSet();
        for (String job : trackedApps) {
            trackedAppInfo.append("<option value='Job ").append(job).append("'>").append(job).append("</option>");
        }

        // timeunit label {2}
        // time unit {3}
        // ajax update time {4}
        // final html
        String trackInfo = MessageFormat.format(trackTemplate, trackedQueueInfo.toString(),
                trackedAppInfo.toString(), timeunitLabel, "" + timeunit, "" + ajaxUpdateTimeMS);
        response.getWriter().println(trackInfo);

        ((Request) request).setHandled(true);
    }

    /**
     * package metrics information in a json and return
     * @param request http request
     * @param response http response
     * @throws java.io.IOException
     */
    private void printJsonMetrics(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/json");
        response.setStatus(HttpServletResponse.SC_OK);

        response.getWriter().println(generateRealTimeTrackingMetrics());
        ((Request) request).setHandled(true);
    }

    public String generateRealTimeTrackingMetrics() {
        // JVM
        double jvmFreeMemoryGB, jvmMaxMemoryGB, jvmTotalMemoryGB;
        if (jvmFreeMemoryGauge == null && metrics.getGauges().containsKey("variable.jvm.free.memory")) {
            jvmFreeMemoryGauge = metrics.getGauges().get("variable.jvm.free.memory");
        }
        if (jvmMaxMemoryGauge == null && metrics.getGauges().containsKey("variable.jvm.max.memory")) {
            jvmMaxMemoryGauge = metrics.getGauges().get("variable.jvm.max.memory");
        }
        if (jvmTotalMemoryGauge == null && metrics.getGauges().containsKey("variable.jvm.total.memory")) {
            jvmTotalMemoryGauge = metrics.getGauges().get("variable.jvm.total.memory");
        }
        jvmFreeMemoryGB = jvmFreeMemoryGauge == null ? 0
                : Double.parseDouble(jvmFreeMemoryGauge.getValue().toString()) / 1024 / 1024 / 1024;
        jvmMaxMemoryGB = jvmMaxMemoryGauge == null ? 0
                : Double.parseDouble(jvmMaxMemoryGauge.getValue().toString()) / 1024 / 1024 / 1024;
        jvmTotalMemoryGB = jvmTotalMemoryGauge == null ? 0
                : Double.parseDouble(jvmTotalMemoryGauge.getValue().toString()) / 1024 / 1024 / 1024;

        // number of running applications/containers
        String numRunningApps, numRunningContainers;
        if (numRunningAppsGauge == null && metrics.getGauges().containsKey("variable.running.application")) {
            numRunningAppsGauge = metrics.getGauges().get("variable.running.application");
        }
        if (numRunningContainersGauge == null && metrics.getGauges().containsKey("variable.running.container")) {
            numRunningContainersGauge = metrics.getGauges().get("variable.running.container");
        }
        numRunningApps = numRunningAppsGauge == null ? "0" : numRunningAppsGauge.getValue().toString();
        numRunningContainers = numRunningContainersGauge == null ? "0"
                : numRunningContainersGauge.getValue().toString();

        // cluster available/allocate resource
        double allocatedMemoryGB, allocatedVCoresGB, availableMemoryGB, availableVCoresGB;
        if (allocatedMemoryGauge == null && metrics.getGauges().containsKey("variable.cluster.allocated.memory")) {
            allocatedMemoryGauge = metrics.getGauges().get("variable.cluster.allocated.memory");
        }
        if (allocatedVCoresGauge == null && metrics.getGauges().containsKey("variable.cluster.allocated.vcores")) {
            allocatedVCoresGauge = metrics.getGauges().get("variable.cluster.allocated.vcores");
        }
        if (availableMemoryGauge == null && metrics.getGauges().containsKey("variable.cluster.available.memory")) {
            availableMemoryGauge = metrics.getGauges().get("variable.cluster.available.memory");
        }
        if (availableVCoresGauge == null && metrics.getGauges().containsKey("variable.cluster.available.vcores")) {
            availableVCoresGauge = metrics.getGauges().get("variable.cluster.available.vcores");
        }
        allocatedMemoryGB = allocatedMemoryGauge == null ? 0
                : Double.parseDouble(allocatedMemoryGauge.getValue().toString()) / 1024;
        allocatedVCoresGB = allocatedVCoresGauge == null ? 0
                : Double.parseDouble(allocatedVCoresGauge.getValue().toString());
        availableMemoryGB = availableMemoryGauge == null ? 0
                : Double.parseDouble(availableMemoryGauge.getValue().toString()) / 1024;
        availableVCoresGB = availableVCoresGauge == null ? 0
                : Double.parseDouble(availableVCoresGauge.getValue().toString());

        // scheduler operation
        double allocateTimecost, handleTimecost;
        if (allocateTimecostHistogram == null
                && metrics.getHistograms().containsKey("sampler.scheduler.operation.allocate.timecost")) {
            allocateTimecostHistogram = metrics.getHistograms()
                    .get("sampler.scheduler.operation.allocate.timecost");
        }
        if (handleTimecostHistogram == null
                && metrics.getHistograms().containsKey("sampler.scheduler.operation.handle.timecost")) {
            handleTimecostHistogram = metrics.getHistograms().get("sampler.scheduler.operation.handle.timecost");
        }
        allocateTimecost = allocateTimecostHistogram == null ? 0.0
                : allocateTimecostHistogram.getSnapshot().getMean() / 1000000;
        handleTimecost = handleTimecostHistogram == null ? 0.0
                : handleTimecostHistogram.getSnapshot().getMean() / 1000000;
        // various handle operation
        Map<SchedulerEventType, Double> handleOperTimecostMap = new HashMap<SchedulerEventType, Double>();
        for (SchedulerEventType e : SchedulerEventType.values()) {
            String key = "sampler.scheduler.operation.handle." + e + ".timecost";
            if (!handleOperTimecostHistogramMap.containsKey(e) && metrics.getHistograms().containsKey(key)) {
                handleOperTimecostHistogramMap.put(e, metrics.getHistograms().get(key));
            }
            double timecost = handleOperTimecostHistogramMap.containsKey(e)
                    ? handleOperTimecostHistogramMap.get(e).getSnapshot().getMean() / 1000000
                    : 0;
            handleOperTimecostMap.put(e, timecost);
        }

        // allocated resource for each queue
        Map<String, Double> queueAllocatedMemoryMap = new HashMap<String, Double>();
        Map<String, Long> queueAllocatedVCoresMap = new HashMap<String, Long>();
        for (String queue : wrapper.getQueueSet()) {
            // memory
            String key = "counter.queue." + queue + ".allocated.memory";
            if (!queueAllocatedMemoryCounterMap.containsKey(queue) && metrics.getCounters().containsKey(key)) {
                queueAllocatedMemoryCounterMap.put(queue, metrics.getCounters().get(key));
            }
            double queueAllocatedMemoryGB = queueAllocatedMemoryCounterMap.containsKey(queue)
                    ? queueAllocatedMemoryCounterMap.get(queue).getCount() / 1024.0
                    : 0;
            queueAllocatedMemoryMap.put(queue, queueAllocatedMemoryGB);
            // vCores
            key = "counter.queue." + queue + ".allocated.cores";
            if (!queueAllocatedVCoresCounterMap.containsKey(queue) && metrics.getCounters().containsKey(key)) {
                queueAllocatedVCoresCounterMap.put(queue, metrics.getCounters().get(key));
            }
            long queueAllocatedVCores = queueAllocatedVCoresCounterMap.containsKey(queue)
                    ? queueAllocatedVCoresCounterMap.get(queue).getCount()
                    : 0;
            queueAllocatedVCoresMap.put(queue, queueAllocatedVCores);
        }

        // package results
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append("\"time\":").append(System.currentTimeMillis()).append(",\"jvm.free.memory\":")
                .append(jvmFreeMemoryGB).append(",\"jvm.max.memory\":").append(jvmMaxMemoryGB)
                .append(",\"jvm.total.memory\":").append(jvmTotalMemoryGB).append(",\"running.applications\":")
                .append(numRunningApps).append(",\"running.containers\":").append(numRunningContainers)
                .append(",\"cluster.allocated.memory\":").append(allocatedMemoryGB)
                .append(",\"cluster.allocated.vcores\":").append(allocatedVCoresGB)
                .append(",\"cluster.available.memory\":").append(availableMemoryGB)
                .append(",\"cluster.available.vcores\":").append(availableVCoresGB);

        for (String queue : wrapper.getQueueSet()) {
            sb.append(",\"queue.").append(queue).append(".allocated.memory\":")
                    .append(queueAllocatedMemoryMap.get(queue));
            sb.append(",\"queue.").append(queue).append(".allocated.vcores\":")
                    .append(queueAllocatedVCoresMap.get(queue));
        }
        // scheduler allocate & handle
        sb.append(",\"scheduler.allocate.timecost\":").append(allocateTimecost);
        sb.append(",\"scheduler.handle.timecost\":").append(handleTimecost);
        for (SchedulerEventType e : SchedulerEventType.values()) {
            sb.append(",\"scheduler.handle-").append(e).append(".timecost\":").append(handleOperTimecostMap.get(e));
        }
        sb.append("}");
        return sb.toString();
    }

    /**
     * package metrics information for one tracked queue/app
     * only support FairScheduler currently
     * @throws java.io.IOException
     */
    private void printJsonTrack(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/json");
        response.setStatus(HttpServletResponse.SC_OK);

        StringBuilder sb = new StringBuilder();
        if (schedulerMetrics instanceof FairSchedulerMetrics) {
            String para = request.getParameter("t");
            if (para.startsWith("Job ")) {
                String appId = para.substring("Job ".length());

                sb.append("{");
                sb.append("\"time\": ").append(System.currentTimeMillis()).append(",");
                sb.append("\"appId\": \"").append(appId).append("\"");
                for (String metric : this.schedulerMetrics.getAppTrackedMetrics()) {
                    String key = "variable.app." + appId + "." + metric;
                    sb.append(",\"").append(metric).append("\": ");
                    if (metrics.getGauges().containsKey(key)) {
                        double memoryGB = Double.parseDouble(metrics.getGauges().get(key).getValue().toString())
                                / 1024;
                        sb.append(memoryGB);
                    } else {
                        sb.append(-1);
                    }
                }
                sb.append("}");

            } else if (para.startsWith("Queue ")) {
                String queueName = para.substring("Queue ".length());
                sb.append("{");
                sb.append("\"time\": ").append(System.currentTimeMillis()).append(",");
                sb.append("\"queueName\": \"").append(queueName).append("\"");
                for (String metric : this.schedulerMetrics.getQueueTrackedMetrics()) {
                    String key = "variable.queue." + queueName + "." + metric;
                    sb.append(",\"").append(metric).append("\": ");
                    if (metrics.getGauges().containsKey(key)) {
                        double memoryGB = Double.parseDouble(metrics.getGauges().get(key).getValue().toString())
                                / 1024;
                        sb.append(memoryGB);
                    } else {
                        sb.append(-1);
                    }
                }
                sb.append("}");
            }
        }
        String output = sb.toString();
        if (output.isEmpty()) {
            output = "[]";
        }
        response.getWriter().println(output);
        // package result
        ((Request) request).setHandled(true);
    }
}