Tags

, , , , , ,


Introduction

Previous article (Part 1) – Carbon Graphite and Message Queuing

Recently I wrote about a graphing product called “Graphite” which enabled me to graph system performance metrics from a database backend managed by a server process called “Carbon”. The Carbon daemon collected its data from a RabbitMQ Message Queuing system, which is used as a central router between servers providing metrics and applications that process the metrics.

In the case of the “Dashing” based environment I setup, the servers have “collectd” agents that collect raw stats and routes them to the queuing server using its AMQP plugin (See here for AMQP technical information). Few people do this but its the only way to scale a system to be able to support 1000’s of servers.

To extend on that capability I have been researching dashboards, and decided that I liked the “Dashing” Dashboard released to the public Domain by Shopify.

dashing-demo-dashboardDashing in a Nutshell:

  1. The dashboard has widgets you can move around thanks to a Javascript library called “Gridster”, that manages the widget size and grid location.
  2. The Widgets are written in Coffeescript which compiles to Javascript.
  3. The dashboard definition only takes a few lines of HTML, nice and compact, the look is via CSS and the communications to the backend to get updates for the widgets is via Server Sent Events (more on this later).
  4. Javascript powers the dashboard on the client and consists of JQuery, Batman.js, Rickshaw graphing library, the JQuery Knob library, the widget code and some extra libraries to do the more fancy widgets and a loader. All this cobbled together and downloaded in one big file.
  5. The backend running on the server is an application written in Ruby which pumps out the SSE updates every second or two. It appears to have a management port which can be used to pump data into it but I did not both to examine that code.

So the only problem(s) in the design of “Dashing” is it powered by a Ruby backend, the Widgets are written in Coffeescript and compiled and it uses SSE that’s not supported correctly on all browser (mainly IE), not that I use IE (I don’t!) and as much as I’m a guru on Ruby (I’m not) I spent enough years looking after a number of obsolete Ruby on Rails legacy system to know its going to be an issue in a production  environment, especially when you need to upgrade the OS over the years and do maintenance… so I began to research a way to replace it, using PHP and add my own Javascript widgets… but first to work out how it all hangs together.

Dashing – Reverse Engineering 101 – a very quick overview!

First… the html

The dashboard consists of a very simple HTML file that lays out and describes the controls needed and links to the Javascript library. Here is a minimal dashboard I knocked together…

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

<title></title>

<script type="text/javascript" src="/app2.js"></script>
<link rel="stylesheet" href="/application.css">

<link href='//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700' rel='stylesheet' type='text/css'>
<link rel="icon" href="/assets/favicon.ico">

</head>
<body>
<div id="container">
<div class="gridster">
<ul>

<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
<div data-id="synergy" data-view="Meter" data-title="Memory Usage" data-min="0" data-max="1000"></div>
</li>

<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
<div data-id="text" data-view="Text" data-title="Comments" data-text="shiny new dashboard."></div>
</li>

<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
<div data-id="convergence" data-view="Graph" data-title="Processes" style="background-color:#000000;"></div>
</li>

<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
<div data-id="karma" data-view="Number" data-title="Karma" style="background-color:#96bf48;"></div>
<i class="icon-heart icon-background"></i>
</li>
</ul>
</div>
</div>
</body>
</html>

The widgets are in the <LI> tags (there are 4 of them) and data binding is used to associate the Server Sent Event data (more on that later) to the right widget using the “data-id” attribute.

The JavaScript

One file is used to hold all the Javascript, and it has all the different libraries concatenated together (I assume this is to speed load time, instead of multiple loading calls in the HTML file to each library). Its odd looking at the file, some of it is verbose and many sections are compressed to save download times, but basically the file has the JQuery minimal code as well as all the libraries mentioned above and the final initialization code at the end. I modified the library to call my eventing code and add some new widgets, so its now called app2.js

<script type="text/javascript" src="/app2.js"></script>

Getting up to speed on Coffee Script

I had a vague memory of Coffee script, so I looked it up during my research, in a nut shell it compiles a JavaScript like language into JavaScript, then its all appended into the JavaScript library file manually with a wrapper line at the start and end of each widget so that the widget construction code is called and eventually a loader renders the widgets. There is also code to attach the widget into the eventing system to get data updates.

Here is a subset of a block of (Widget) code generated by the compiler, its for displaying “Lists” in a widget and relies on some inherited widget code to do most of the work:

(function() {
  var _ref,
    __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } 
  function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  Dashing.List = (function(_super) {
    __extends(List, _super);

    function List() {
      _ref = List.__super__.constructor.apply(this, arguments);
      return _ref;
    }

    List.prototype.ready = function() {
      if (this.get('unordered')) {
        return $(this.node).find('ol').remove();
      } else {
        return $(this.node).find('ul').remove();
      }
    };
    return List;
  })(Dashing.Widget);
}).call(this);

Server-Sent Events…. rapid learning and becoming an expert…

Even though I did postgrad in Web Services I had not heard of SSE, then a review of some Javascript code revealed its just AJAX with a twist. Instead of opening a connection, sending a request and getting back XML then closing the connection, the open call keeps the connection open for up to 5 minutes and the server sends back JSON strings appended to a “data:” string which is then processed by the batman.js library to insert the JSON into the DOM, the widgets are part of the DOM so the widgets get their dataset updated.

When the connection is closed at the timeout period, it is re-opened and the process repeats. The Javascript code looks like this:

source = new EventSource('/events.php');

  source.addEventListener('open', function(e) {
    return console.log("Connection opened", e);
  });

  source.addEventListener('error', function(e) {
    console.log("Connection error", e);
    if (e.currentTarget.readyState === EventSource.CLOSED) {
      console.log("Connection closed");
      return setTimeout((function() {
        return window.location.reload();
      }), 5 * 60 * 1000);
    }
  });

  source.addEventListener('message', function(e) {
    var data, widget, _i, _len, _ref1, _ref2, _ref3, _results;
    data = JSON.parse(e.data);
    if (((_ref1 = lastEvents[data.id]) != null ? _ref1.updatedAt : void 0) !== data.updatedAt) {
      if (Dashing.debugMode) {
        console.log("Received data for " + data.id, data);
      }

In “Dashing” the SSE are sent by a Ruby App, but I wrote a PHP one called it events.php (as shown in the code above) to return the data in the same format at set intervals, you can send data for every widget, 1 line at a time. In the code above you can see that the JSON code returned is parsed, if you turn on the browser javascript console and set the debug flag in the code you can see the data flow into the web browser.

Below is a sample of the output for the Dashboard code (events.php) from above:

# php events.php
data: {“text”:167,”id”:”text”,”updatedAt”:1392203738}

data: {“value”:167,”id”:”synergy”,”updatedAt”:1392203738}

data: {“current”:43,”last”:0,”id”:”karma”,”updatedAt”:1392203738}

data: {“points”:[{“x”:5,”y”:18},{“x”:10,”y”:0},{“x”:15,”y”:19},{“x”:20,”y”:13},{  “x”:25,”y”:14},{“x”:30,”y”:16},{“x”:35,”y”:16},{“x”:40,”y”:0},{“x”:45,”y”:15},{”  x”:50,”y”:0}],”id”:”convergence”,”updatedAt”:1392203738}

So that’s it in a nutshell, I have since found “Team Dashboard” which is a deviation from Dashing and looks much more workable, does not use SSE and each widget appears to gather its own event data. I will be doing an analysis of it and post it on the blog soon enough.

Some links to the various technologies:

-oOo-

Advertisements