Package com.sun.tools

Source Code of com.sun.tools.MemcacheStats$MemCacheTask

/* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://www.sun.com/cddl/cddl.html or
* install_dir/legal/LICENSE
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at install_dir/legal/LICENSE.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
*/
package com.sun.tools;

import com.sun.faban.common.Command;
import com.sun.faban.common.CommandHandle;
import com.sun.faban.common.NameValuePair;
import com.sun.faban.common.TextTable;

import com.sun.faban.harness.ConfigurationException;
import com.sun.faban.harness.Context;
import com.sun.faban.harness.Configure;
import com.sun.faban.harness.Start;
import com.sun.faban.harness.Stop;
import com.sun.faban.harness.tools.ToolContext;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* MemcacheStats.java
* This program implements a tool to collect memcache stats from
* a list of servers. The stats are collected and displayed at the
* specified interval. Most of the stats are on a per. second basis
* allowing easy tabulation and comparison with other tools.
*
* @author Shanti Subramanyam based on work by Kim LiChong
* modified by Sheetal Patil
*
*/
public class MemcacheStats {

    private static StatsClient cache = null;
    private static Logger logger = Logger.getLogger(MemcacheStats.class.getName());
    TextTable outputTextTable = null;
    int interval;
    private SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");

    /** Injected tool context. */
    @Context public ToolContext ctx;
    Command cmd;
    CommandHandle processRef;
    ArrayList<String> toolCmd;
    String toolName;
    private Timer timer;
    private PrintWriter out;
    String[] memCacheServers;
    /**
   * This constructor creates a memcache client with a pool of servers.
     * This is for standalone invocation from main.
     *
     * @param servers Name of servers running memcached
   * @param interval Interval in secs for stats collection
     * @throws IOException Error communicating with memcached
     */
    public MemcacheStats(String servers[], int interval) throws IOException {
        this.interval = interval;
        cache = new StatsClient(servers);
    }

    /**
     * Constructs the memcache stats from the tool agent. A no-arg constructor
     * is required in this case. Arguments are passed via calling the configure
     * method.
     */
    public MemcacheStats() {

    }

    /*
     * Columns in which the various stats fields will appear
     */
    private static final int CURTIME = 1;
    private static final int CUR_ITMS = 2;
    private static final int BYTES = 3;
    private static final int CUR_CONNS = 4;
    private static final int SETS = 5;
    private static final int GETS = 6;
    private static final int GET_HITS = 7;
    private static final int GET_MISSES = 8;
    private static final int EVICTS = 9;
    private static final int BYTES_R = 10;
    private static final int BYTES_W = 11;

    private static final int NUM_COLS = 11;
    long[][] previousStats;
   
    /**
   * This method is used for dynamic memcache stats gathering.
     * We only gather and print out the following stats in a single row:
     * cur_itms bytes cur_conns sets gets get_hits get_misses evicts bytes_r, bytes_w
     * Since memcached returns cumulative stats, we do the subtraction to get
     * the stats for this interval and then figure out stats/sec
     * @return TextTable - a single row of stats
     */
    public TextTable getStats() {
        Integer intval = 0;
    long longval = 0;
    double dblval = 0;
    DecimalFormat decval = new DecimalFormat("0.00");
    FieldPosition fld = new FieldPosition(DecimalFormat.INTEGER_FIELD);

        Map memcacheStats = cache.stats();
        //cache.stats() will return a Map whose key  is the name of the memcache server
        //and whose value is a Map with the memcache statistics

        Set<Map.Entry> serverEntries = memcacheStats.entrySet();

        //set counter to allow to set number of columns to output
        int row = 0;

        //reset the iterator
        for (Map.Entry serverEntry : serverEntries) {
            String key = (String) serverEntry.getKey();
            if (key == null)
                break;
            Map statsMap = (Map) serverEntry.getValue();
            // get rid of the ":<port>"
            String srv = key.substring(0, key.indexOf(":"));
            if (srv == null)
        break;

            if (outputTextTable == null) {
                // the number of rows is #of servers (for each interval)
                // One extra column for server name
                outputTextTable = new TextTable(serverEntries.size(), NUM_COLS + 1);

                // the number of columns is the stats that we gather
                //set Header
                outputTextTable.setHeader(0, "Server");
                outputTextTable.setHeader(CURTIME, "Time");
                outputTextTable.setHeader(CUR_ITMS, "items");
                outputTextTable.setHeader(BYTES, "cache_MB");
                outputTextTable.setHeader(CUR_CONNS, "conns");
                outputTextTable.setHeader(SETS, "sets/s");
                outputTextTable.setHeader(GETS, "gets/s");
                outputTextTable.setHeader(GET_HITS, "get_hits/s");
                outputTextTable.setHeader(GET_MISSES, "get_misses/s");
                outputTextTable.setHeader(EVICTS, "evicts/s");
                outputTextTable.setHeader(BYTES_R, "rB/s");
                outputTextTable.setHeader(BYTES_W, "wB/s");
               
                previousStats = new long[serverEntries.size()][NUM_COLS+1];
            }
            outputTextTable.setField(row, 0, key);

            //get this value's iterator
            Set<Map.Entry> statsMapEntries = statsMap.entrySet();

            // Populate the rest of the table.
            for (Map.Entry statsMapEntry : statsMapEntries) {
            StringBuffer str = new StringBuffer();
                String fldKey = statsMapEntry.getKey().toString();
                String fldValue = ((CharSequence)(statsMapEntry.getValue())).toString();
                logger.fine("key = " + fldKey + ", value = " + fldValue);

        /*
                 * NOTE: Although it seems that we're doing a portion of the same
                 * code (setting val, curVal) in all of the if statements, we can't
                 * take it out in a common way. This is because other stats that
                 * we're not looking at are not integers.
         */
         /* We do absolute stats for CUR_ITMS,BYTES and CUR_CONNS */
                if (fldKey.equals("time")) {
                    longval = Long.parseLong(fldValue) * 1000; // sec to ms
                    outputTextTable.setField(row, CURTIME,
                                                df.format(new Date(longval)));
                } else if (fldKey.equals("curr_items")) {
                    intval = Integer.parseInt(fldValue);
                    outputTextTable.setField(row, CUR_ITMS, intval.toString());
                } else if (fldKey.equals("bytes")) {
        // bytes can be large, so store in long - convert to MBs
                    longval = Long.parseLong(fldValue);
          dblval = (double)longval / 1000000;
          decval.format(dblval, str, fld);
                    outputTextTable.setField(row, BYTES, str);
                } else if (fldKey.equals("curr_connections")) {
                    intval = Integer.parseInt(fldValue);
                    outputTextTable.setField(row, CUR_CONNS, intval.toString());
                } else if (fldKey.equals("cmd_set")) {
                    longval = Long.parseLong(fldValue);
                    dblval = (double)(longval - previousStats[row][SETS])/interval;
          decval.format(dblval, str, fld);
                    outputTextTable.setField(row, SETS, str);
                    previousStats[row][SETS] = longval;
                } else if (fldKey.equals("cmd_get")) {
                    longval = Long.parseLong(fldValue);
                    dblval = (double)(longval - previousStats[row][GETS])/interval;
          decval.format(dblval, str, fld);
                    outputTextTable.setField(row, GETS, str);
                    previousStats[row][GETS] = longval;
                } else if (fldKey.equals("get_hits")) {
                    longval = Long.parseLong(fldValue);
                    dblval = (double)(longval - previousStats[row][GET_HITS])/interval;
          decval.format(dblval, str, fld);
                    outputTextTable.setField(row, GET_HITS, str);
                    previousStats[row][GET_HITS] = longval;
                } else if (fldKey.equals("get_misses")) {
                    longval = Long.parseLong(fldValue);
                    dblval = (double)(longval - previousStats[row][GET_MISSES])/interval;
          decval.format(dblval, str, fld);
                    outputTextTable.setField(row, GET_MISSES, str);
                    previousStats[row][GET_MISSES] = longval;
                } else if (fldKey.equals("evictions")) {
                    longval = Long.parseLong(fldValue);
                    intval = (int)((longval - previousStats[row][EVICTS])/interval);
                    outputTextTable.setField(row, EVICTS, intval.toString());
                    previousStats[row][EVICTS] = longval;
                } else if (fldKey.equals("bytes_read")) {
                    longval = Long.parseLong(fldValue);
                    intval = (int)((longval - previousStats[row][BYTES_R])/interval);
                    outputTextTable.setField(row, BYTES_R, intval.toString());
                    previousStats[row][BYTES_R] = longval;
                } else if (fldKey.equals("bytes_written")) {
                    longval = Long.parseLong(fldValue);
                    intval = (int)((longval - previousStats[row][BYTES_W])/interval);
                    outputTextTable.setField(row, BYTES_W, intval.toString());
                    previousStats[row][BYTES_W] = longval;
                }
                // Some version of memcached do not have evicts.
                if (outputTextTable.getField(row, EVICTS) == null)
                    outputTextTable.setField(row, EVICTS, "-");
            }
            row++;
        }
        return outputTextTable;
    }

    /**
     * This main method is used to gather dynamic statistics on memcache server instances.
   * The primary arguments are the names of the memcached servers
     *  It accepts the following optional argument:
     * -i interval   the snapshot period to collect the stats, in seconds (default 10)
     *  Usage:  java com.sun.faban.harnes.tools.MemcacheStats host[:port]... [-i interval]
   * It creates an instance of MemcacheStats and sets up a timer task
   * at the specified interval to gather the stats.
     *  @param args String []
     *
     */
    public static void main(String[] args) {
        int intervalTime = 10000; // in msecs
        LinkedHashSet<String> serverSet = new LinkedHashSet<String>();

        if (args==null || args.length < 1) {
            System.err.println("Usage: java com.sun.faban.harness.tools.MemcacheStats host[:port]... [-i interval]");
                System.exit(1);
        }
        for (int i = 0; i < args.length; i++) {
            if (args[i].startsWith("-i")) {
                if (args[i].length() > 2) // -iarg
                {
                    intervalTime =
                            Integer.parseInt(args[i].substring(2)) * 1000;
                } else // -i arg
                {
                    intervalTime = Integer.parseInt(args[++i]) * 1000;
                }
            } else if (args[i].contains(":")) {// host:port pair
                serverSet.add(args[i]);
            } else { // host only. Append default port 11211.
                serverSet.add(args[i] + ":11211");
            }
        }
        //finished processing all of the args.  populate server list
        String memCacheServers[] = new String[serverSet.size()];
        memCacheServers = serverSet.toArray(memCacheServers);

        /*logger.fine("Starting memcache stats");

        MemcacheStats memcacheStats = new MemcacheStats(memCacheServers, intervalTime/1000);

        try {
            Timer timer = new Timer();
            MemCacheTask task = new MemCacheTask(memcacheStats);
            timer.scheduleAtFixedRate(task, 0, intervalTime);

        } catch (Exception ex) {
            logger.severe("Exception in setting up timer " + ex);
            logger.log(Level.FINE, "Exception", ex);
            return;
        }*/
    }

    /**
     * Configures this MemcacheStats tool.
     */
    @Configure public void configure() throws ConfigurationException {
        LinkedHashSet<String> serverSet = new LinkedHashSet<String>();
        List<String> toolArgs = ctx.getToolArgs();
        if(toolArgs == null){
            throw new ConfigurationException("MemcacheStats toolArgs is not provided");
        }
        List<NameValuePair<Integer>> myHostPorts =
                ctx.getServiceContext().getUniqueHostPorts();
        if(myHostPorts == null){
            throw new ConfigurationException("Memcached host:port is not provided");
        }
        for(NameValuePair<Integer> myHostPort : myHostPorts){
            if(myHostPort.value == null)
                myHostPort.value = 11211;
            serverSet.add(myHostPort.name +":"+myHostPort.value);
        }
        String[] args = new String[toolArgs.size()];
        for(int i=0; i < toolArgs.size(); i++){
            args[i] = toolArgs.get(i);
        }
        int intervalTime = 10000; // in msecs


        if (args==null || args.length < 1) {
            logger.log(Level.INFO, "MemcacheStats [-i interval]");
        }
        for (int i = 0; i < args.length; i++) {
            if (args[i].startsWith("-i")) {
                if (args[i].length() > 2) // -iarg
                {
                    intervalTime =
                            Integer.parseInt(args[i].substring(2)) * 1000;
                } else // -i arg
                {
                    intervalTime = Integer.parseInt(args[++i]) * 1000;
                }
            }
        }
        //finished processing all of the args.  populate server list
        memCacheServers = new String[serverSet.size()];
        memCacheServers = serverSet.toArray(memCacheServers);
        interval = intervalTime/1000;
        // Trim host:ports down to the ones pertaining to this host.
        // Parse interval from the mcstat command from the tool context.
        // store interval/1000 and host ports in field interval and ports.
    }

    /**
     * Starts monitoring the memcached instances.
     * @throws IOException Error communicating with memcached
     */
    @Start public void start() throws IOException {
        logger.fine("Starting memcache stats");
        // MemcacheStats memcacheStats = new MemcacheStats(memCacheServers, intervalTime/1000);
        cache = new StatsClient(memCacheServers);

        try {
            timer = new Timer();
            out = new PrintWriter(ctx.getOutputFile());
            MemCacheTask task = new MemCacheTask(this, out);
            timer.scheduleAtFixedRate(task, 0, interval);

        } catch (Exception ex) {
            logger.severe("Exception in setting up timer " + ex);
            logger.log(Level.FINE, "Exception", ex);
            return;
        }
    }

    /**
     * Stops monitoring the memcached instances.
     */
    @Stop public void stop() {
        timer.cancel();
        out.flush();
        out.close();
    }

    /* class for TimerTask */
    private static class MemCacheTask extends TimerTask {

        private MemcacheStats memcacheStats;
        private PrintWriter out;
        public MemCacheTask(MemcacheStats memcacheStats, PrintWriter out) {
            this.memcacheStats = memcacheStats;
            this.out = out;
        }

        public void run() {
            out.println(memcacheStats.getStats());
        }
    }

    /**
     * The client code to interface with all memcached servers.
     */
    private static class StatsClient {

        ArrayList<StatsConnection> connections;
        Map<String, Map<String, String>> stats;

        /**
         * Constructs the client for all given servers.
         * @param servers host:port pairs for the server
         * @throws IOException Error communicating with memcached
         */
        public StatsClient(String[] servers) throws IOException {
            connections = new ArrayList<StatsConnection>(servers.length);
            stats = new LinkedHashMap<String, Map<String, String>>(servers.length);
            for (String server : servers)
                try {
                    StatsConnection conn = new StatsConnection(server);
                    connections.add(conn);
                    stats.put(server, conn.result);
                } catch (IOException e) {
                    logger.log(Level.SEVERE, "Cannot connect to " + server +
                                ".", e);
                }
            if (connections.size() == 0)
                throw new IOException("No host available.");
        }

        /**
         * Constructs the client for the given servers on the local system.
         * @param ports The list of ports
         * @throws IOException Error communicating to memcached
         */
        public StatsClient(ArrayList<Integer> ports) throws IOException {
            connections = new ArrayList<StatsConnection>(ports.size());
            stats = new LinkedHashMap<String, Map<String, String>>(ports.size());
            for (int port : ports)
                try {
                    StatsConnection conn = new StatsConnection("localhost", port);
                    connections.add(conn);
                    stats.put("localhost:" + port, conn.result);
                } catch (IOException e) {
                    logger.log(Level.SEVERE, "Cannot connect to " + port +
                                ".", e);
                }
            if (connections.size() == 0)
                throw new IOException("No host available.");
        }

        /**
         * Obtains the stats from all servers.
         * @return the stats from all servers.
         */
        public Map<String, Map<String, String>> stats() {
            for (StatsConnection connection : connections) {
                try {
                    connection.stats();
                } catch (IOException e) {
                    logger.log(Level.SEVERE, "Error obtaining stats from " +
                            connection.server + ".", e);
                }
            }
            return stats;
        }
    }

    private static class StatsConnection {

        String server;
        Socket socket;
        BufferedReader reader;
        OutputStream out;
        HashMap<String, String> result = new HashMap<String, String>();

        static final byte[] cmd = "stats\r\n".getBytes();

        StatsConnection(String server) throws IOException {
            this.server = server;
            int colIdx = server.indexOf(':');
            String host = server.substring(0, colIdx);
            int port = Integer.parseInt(server.substring(colIdx + 1));
            init(host, port);
        }

        StatsConnection(String host, int port) throws IOException {
            server = host + ':' + port;
            init(host, port);
        }

        private void init(String host, int port) throws IOException {
            socket = new Socket(host, port);
            out = socket.getOutputStream();
            reader = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));
        }

        Map<String, String> stats() throws IOException {
            out.write(cmd);
            for (;;) {
                String line = reader.readLine();
                if ("END".equals(line))
                    break;
                StringTokenizer t = new StringTokenizer(line);
                String statStr = t.nextToken();
                if (!"STAT".equals(statStr))
                    throw new IOException("Expecting STAT, got " + statStr);
                String key = t.nextToken();
                String value = t.nextToken();
                result.put(key, value);
            }
            return result;
        }
    }
}
TOP

Related Classes of com.sun.tools.MemcacheStats$MemCacheTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.