Thursday, 19 February 2015

Embedding HTML5 video is still pretty hard


In the early days of the HTML5 movement, I wrote the first major cross-browser compatibility shim for HTML5 <video> and <audio> tags. It was called html5media.js.
At the time, I assumed that the shim would be obsolete within a few years, just as soon as major browsers adopted a common standard and video codec. Unfortunately, the shim is still used by hundreds of thousands of people each day, and embedding video is just as confusing as ever.

So how do I embed video in my site?

Please, just save yourself a headache, and host your video on YouTubeVimeo, or some other third party service. They employ some very clever people who’ve solved all the problems with embedding video.

Haha… no, really. How do I embed video in my site?

Take a deep breath. In order to embed video in your site, there are four major groups of people you need to keep happy:
  1. Modern browsers using commercial codecs (Chrome, Safari, IE9+)
  2. Modern browsers using open-source codecs (Firefox, Opera)
  3. Legacy browsers (IE8)
  4. Under-powered mobile devices (iPhone 3GS, cheap Android)
For the rest of this post, I’ll take you through the steps required to allow an increasing number of people to watch your video.

Embedding video for modern browsers with commercial codecs

The simplest video embed code you can possibly use is as follows:
1
2
3
4
5
6
<!DOCTYPE html>
<html>
    <body>
        <video src="video.mp4" width=640 height=360 controls>
    </body>
</html>
Congratulations! Your video will now play in:
  • Chrome
  • Safari (inc. Mobile Safari on iPhone 4+)
  • IE9+

Adding support for legacy browsers

In order to make your video work in legacy browsers, you need to add a script tag to the <head>of your document. This script, the venerable html5media.js, will provide a Flash video player fallback for legacy browsers.
1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
    <head>
        <script src="http://api.html5media.info/1.1.5/html5media.min.js"></script>
    </head>
    <body>
        <video src="video.mp4" width=640 height=360 controls></video>
    </body>
</html>
Note: The syntax of the <video> tag has changed to include an explicit closing tag, to avoid confusing older browsers.
Fantastic! Your video will now play in:
  • Chrome
  • Safari (inc. Mobile Safari on iPhone 4+)
  • IE9+
  • IE8 (via Flash)
  • Firefox (via Flash)
  • Opera (via Flash)
At this point, the vast majority of internet users will be able to play your video. The only people who’ll be left out will be:
  • Firefox or Opera users without Flash
  • Owners of under-powered mobile devices.

Adding Flash-free support for modern browers with open-source codecs

To allow Firefox and Opera users to view your video using their native players, you need to transcode your video into an open-source format, and embed both files in your page. I’d recommend using the free Miro Video Encoder to transcode your video to WebM format. You can then embed it using the following code:
1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
    <head>
        <script src="http://api.html5media.info/1.1.5/html5media.min.js"></script>
    </head>
    <body>
        <video src="video.mp4" width=640 height=360 controls>
            <source src="video.mp4"></source>
            <source src="video.webm"></source>
        </video>
    </body>
</html>
Note: We’re adding explicit closing tags to <source> elements to avoid confusing legacy browsers.
Unbelievable! Now your video will play in:
  • Chrome
  • Safari (inc. Mobile Safari on iPhone 4+)
  • IE9+
  • IE8 (via Flash)
  • Firefox (via Flash)
  • Opera (via Flash)
It’s just the owners of under-powered mobile devices who’ll struggle to play your video now.

Adding support for under-powered mobile devices

The latest mobile devices support high-resolution video, but cheap Android phones and iPhone 3GS will refuse to play anything higher-resolution than about 320 x 180 pixels. To keep these devices happy, you need to transcode your video to this lower resolution. Miro Video Encoderhas a built-in iPhone 3GS setting, so just use that.
Now you can embed your video using the following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
    <head>
        <script src="http://api.html5media.info/1.1.5/html5media.min.js"></script>
    </head>
    <body>
        <video src="video.mp4" width=640 height=360 controls>
            <source src="video.mp4" media="only screen and (min-device-width: 568px)"></source>
            <source src="video-low.mp4" media="only screen and (max-device-width: 568px)"></source>
            <source src="video.webm"></source>
        </video>
    </body>
</html>
OMG! What a monster! But now everyone will be able to play your video!
  • Chrome
  • Safari (inc. Mobile Safari on iPhone 4+)
  • IE9+
  • IE8 (via Flash)
  • Firefox (via Flash)
  • Opera (via Flash)
  • Mobile Safari (iPhone 3GS)
  • Android Browser (inc. cheap Android phones)

Wednesday, 18 February 2015

A Beginner's Tutorial on Creating WCF REST Services

Introduction

In this article we will try to understand what are WCF REST services. We will see what is required from a service developers perspective to create a REST enabled WCF service. We see how we can use and consume restful WCF services.

Background

Overview of REST

REST stands for Representational State Transfer. This is a protocol for exchanging data over a distributed environment. The main idea behind REST is that we should treat our distributed services as a resource and we should be able to use simple HTTP protocols to perform various operations on that resource.
When we talk about the Database as a resource we usually talk in terms of CRUD operations. i.e. Create, Retrieve, Update and Delete. Now the philosophy of REST is that for a remote resource all these operations should be possible and they should be possible using simple HTTP protocols.
Now the basic CRUD operations are mapped to the HTTP protocols in the following manner:
  • GET: This maps to the R(Retrieve) part of the CRUD operation. This will be used to retrieve the required data (representation of data) from the remote resource.
  • POST: This maps to the U(Update) part of the CRUD operation. This protocol will update the current representation of the data on the remote server.
  • PUT: This maps to the C(Create) part of the CRUD operation. This will create a new entry for the current data that is being sent to the server.
  • DELETE: This maps to the D(Delete) part of the CRUD operation. This will delete the specified data from the remote server.
so if we take an hypothetical example of a remote resource that contain a database of list of books. The list of books can be retrieved using a URL like:
www.testwebsite.com/books
To retrieve any specific book, lets say we have some ID that we can used to retrieve the book, the possible URL might look like:
www.testwebsite.com/books/1
Since these are GET requests, data can only be retrieved from the server. To perform other operations, if we use the similar URI structure with PUTPOST or DELETE operation, we should be able to create, update and delete the resource form the server. We will see how this can be done in implementation part.
Note: A lot more complicated queries can be performed using these URL structures. we will not be discussing the complete set of query operations that can be performed using various URL patterns.

Using the code

Now we can create a simple WCF service that will implement all the basic CRUD operations on some database. But to make this WCF service REST compatible we need to make some changes in the configuration, service behaviors and contracts. Let us see what WCF service we will be creating and then we will see how we can make useful over the REST protocol.

creating REST enabled ServiceContract

We will create Books table and will try to perform CRUD operations on this table.

To perform the Database operations within the service lets use Entity framework. This can very well be done by using ADO.NET calls or some other ORM but I chose entity framework. (please refer this to know about entity framework:  An Introduction to Entity Framework for Absolute Beginners[^]). The generated Entity will look like following.

Now the service contract will contain functions for CRUD operations. Let us create the ServiceContract for this service:
[ServiceContract]
public interface IBookService
{
    [OperationContract]
    List<Book> GetBooksList();

    [OperationContract]
    Book GetBookById(string id);

    [OperationContract]
    void AddBook(string name);

    [OperationContract]
    void UpdateBook(string id, string name);

    [OperationContract]
    void DeleteBook(string id);
}
Right now this is a very simple service contract, to indicate that individual operations can be called using REST protocol, we need to decorate the operations with additional attributes. The operations that are to be called on HTTP GET protocol, we need to decorate them with the WebGet attribute. The operations that will be called by protocols, like POST, PUT, DELETE will be decorated with WebInvoke attribute.

Understanding UriTemplate

Now before adding these attributes to these operations let us first understand the concept of UriTemplate.UriTemplate is a property of WebGet and WebInvoke attribute which will help us to map the parameter names coming from the HTTP protocol with the parameter names of ServiceContract. For example, if someone uses the following URI:
localhost/testservice/GetBookById/2
We need to map this first parameter with the id variable of the function. this can be done using theUriTemplate. Also, we can change the function name specifically for the URI and the name of URI function name will be mapped to the actual function name i.e. if we need to call the same URL as:
localhost/testservice/Book/2
then we can do that by specifying the UriTemplate for the operation as:
[OperationContract]
[WebGet(UriTemplate  = "Book/{id}")]
Book GetBookById(string id);
Following the same lines, let us define the UriTemplate for other methods too.
[ServiceContract]
public interface IBookService
{
    [OperationContract]
    [WebGet]
    List<Book> GetBooksList();

    [OperationContract]
    [WebGet(UriTemplate  = "Book/{id}")]
    Book GetBookById(string id);

    [OperationContract]
    [WebInvoke(UriTemplate = "AddBook/{name}")]
    void AddBook(string name);

    [OperationContract]
    [WebInvoke(UriTemplate = "UpdateBook/{id}/{name}")]
    void UpdateBook(string id, string name);

    [OperationContract]
    [WebInvoke(UriTemplate = "DeleteBook/{id}")]
    void DeleteBook(string id);
}

Implementing the Service  

Now the service implementation part will use the entity framework generated context and entities to perform all the respective operations.
public class BookService : IBookService
{
    public List<Book> GetBooksList()
    {
        using (SampleDbEntities entities = new SampleDbEntities())
        {
            return entities.Books.ToList();
        }
    }

    public Book GetBookById(string id)
    {
        try
        {
            int bookId = Convert.ToInt32(id);

            using (SampleDbEntities entities = new SampleDbEntities())
            {
                return entities.Books.SingleOrDefault(book => book.ID == bookId);
            }
        }
        catch
        {
            throw new FaultException("Something went wrong");
        }
    }

    public void AddBook(string name)
    {
        using (SampleDbEntities entities = new SampleDbEntities())
        {
            Book book = new Book { BookName = name };
            entities.Books.AddObject(book);
            entities.SaveChanges();
        }
    }

    public void UpdateBook(string id, string name)
    {
        try
        {
            int bookId = Convert.ToInt32(id);

            using (SampleDbEntities entities = new SampleDbEntities())
            {
                Book book = entities.Books.SingleOrDefault(b => b.ID == bookId);
                book.BookName = name;
                entities.SaveChanges();
            }
        }
        catch
        {
            throw new FaultException("Something went wrong");
        }
    }

    public void DeleteBook(string id)
    {
        try
        {
            int bookId = Convert.ToInt32(id);

            using (SampleDbEntities entities = new SampleDbEntities())
            {
                Book book = entities.Books.SingleOrDefault(b => b.ID == bookId);
                entities.Books.DeleteObject(book);
                entities.SaveChanges();
            }
        }
        catch
        {
            throw new FaultException("Something went wrong");
        }
    }
}

Restful WCF service Configuration

Now from the ServiceContract perspective the service is ready to serve the REST request but to access this service over rest we need to do some changes in the service behavior and binding too.
To make the service available over REST protocol the binding that needs to be used is the webHttpBinding. Also, we need to set the endpoint's behavior configuration and define the webHttp parameter in theendpointBehavior. So our resulting configuration will look something like:

Test the service

Now to test the service we will simply run the service and use the URLs to retrieve the data. let see this for ourGET operations in action.

And now testing the query to get a single record

And so we have seen that we received the response in the browser itself in form of XML. We can use this service without even consuming it by adding a service reference by using the URLs and HTTP protocols.
Note: Here I am not demonstrating the other operations for POST, PUT and DELETE but they are fairly straight forwards and a simple HTML page sending the data using the required protocol with the specified parameter names will perform the operation.

Using JSON

We can also change the Response and Request format to use JSON instead of XML. To do this we need to specify properties of the WebInvoke attribute.
  • RequestFormat: By default its value is WebMessageFormat.XML. to change it to JSON format, it needs to be set to WebMessageFormat.Json.
  • ResponseFormat: By default its value is WebMessageFormat.XML. to change it to JSON format, it needs to be set to WebMessageFormat.Json.
Let us create one more operation in our service contract called as GetBooksNames and will apply theResponseFormat as Json for this method.
[OperationContract]
[WebGet(ResponseFormat=WebMessageFormat.Json]
List<string> GetBooksNames();
The response will now appear in the JSON format.

And now we have a WCF REST service ready with us.
Note: We also have a ready made template in Visual studio to create WCF data services that provides us with easy way to create REST enabled ODATA services. We will perhaps talk about them separately.

Wednesday, 11 February 2015

Profiling and Logging Entity Framework Queries

So far, in our Entity Framework 4.0 Article series, we have covered some basics of the what, why and how of Entity Framework 4.0 and also performed some CRUD operations with Entity Framework. We have also seen how to create an independent Entity Data Model and bind the Model to controls like the ASP.NET GridView. In this article, we will see how to log the SQL queries that get generated by Entity Framework. We will also list profiling tools available.

With the Entity Framework, you are architecting, designing and developing at a conceptual level, without worrying too much about the specific details of communicating with the database. Entity Framework uses ADO.NET classes (like the SqlClient) behind the scenes to convert code into SQL queries, with the details abstracted from you.
Although the SQL generated by the System.Data.SqlClient has improved in .NET 4.0., it is always a good idea to do query profiling i.e. watch the queries and commands that get executed on the database and improve your code, if needed. You have a couple of options to watch the queries that get generated via Entity Framework. Some of them are:
- Using ObjectQuery.ToTraceString() method
Using Intellitrace (available in VS 2010 Ultimate)
EFTracingProvider (on MSDN code gallery)
In this article, we will see how to watch some of the SQL queries generated using theObjectQuery.ToTraceString() method. We will create a simple logging mechanism that will log the query to a .txt file. You can then send the .txt file to your DBA or check the queries on your own for performance improvements.
Note: The ObjectQuery class implements common functionality for queries against a conceptual model using both LINQ to Entities and ObjectQuery<T>. Read my article Exploring how the Entity Data Model (EDM) Generates Code and Executes Queries – Entity Framework 4.0 to see how ObjectQuery functions.
Lets create our query logging mechanism. Here I am using the same code that I used in one of myprevious articles. Assuming you have downloaded the source code of the previous article, open the ‘ConsoleAppUsingMyModel’ project > right click the project in Solution Explorer > Add > Class. Rename the class to ‘QueryHandler.cs’ and click on ‘Add’. Write the following code in the class
Query Handler
Note: The query handler shown above is a very simple query logging mechanism and can be used by developers on their machines to log queries. In no way, should you use this logging mechanism on your production server. Explore Log4Net for advanced logging scenarios or use the different logging and profiling options listed above.
Now go back to Program.cs and add the following line in the class
ObjectQuery To Trace String
As you can see, to retrieve the query generated, we are casting the result of the LINQ to Entities query to an instance of ObjectQuery class and calling the ToTraceString() method on it. We are then passing this string to the QueryHandler.WriteQuery() method to log it. Only one log file gets generated for each day the application is run.
Note: You can see only some queries using the ToTraceString() method. For eg: Queries which make use of Single() or make use of Lazy loading or insert, update, delete are not logged.
That’s it. Run the application and open the log file for that day and check the query generated
Log Entry
Although Entity Framework generates the query for you, it’s important to be aware of what’s happening in your database! In this article, we explored how to log simple queries. For advanced profiling scenarios, feel free to explore the other tools I mentioned at the beginning of this article.
The entire source code of this article can be downloaded over here

Wednesday, 4 February 2015

Durable AMQP Applications -RabbitMQ



We've started using RabbitMQ at my day job. The initial use case is to stream create/update/delete events (like a firehose) to other parts in the system. This is a mission critical queue--we can't afford to lose messages. This post explains how to create RabbitMQ producers with failure handling and durability in mind.
Let's start at the beginning. There is a connection to the server. You open a channel between your application and the connection. Exchanges communicate over the channel. Queues bind to exchanges. Messages are sent over queues.
Exchanges & queues are ephemeral by default. They live and die with the application the process. This means they will not survive application or server restarts. You can declare them as durable. Durable items live outside of processes and server. So if the process dies or the server restarts things will be as they were. Durable queues and exchanges are the first step to a robust application.
Messages may also be "durable". Publish messages with the :persistent flag and RabbitMQ will write the message to disk. The messages are loaded from disk when server restarts and sent to any existing queues.
Durable queues/exchanges and persistent messages will get you pretty far. They keep things working under normal conditions. There is another problem: network issues. What happens when the connection is lost? How does the application reconnect? What happens to messages?
Application crashes and network issues are common. The amqp gem implements a robust recovery protocol. In fact, it can recover from network issues automatically when configured. The amqp will reconnect to the server, redeclare exchanges, and any queues automatically. There is still one problem: messages produced during a connection outage are lost.
Unfortunately the amqp gem cannot help here. You may think: I have the :persistentoption, my messages are safe. This is incorrect. The messages are only persisted on the server when the server is connected. We need to handle this ourselves. The application must buffer its messages while the connection is down. Then empty the buffer when reconnection happens. There is another caveat here: what happens if the application crashes or exits before buffer is drained? The buffer itself should be persistent. This way the undelivered messages will survive application crashes, server crashes, connection losses, and application/server crashes during a connection loss.
This may seem like overkill. I assure you it's not for mission critical messages. This is responsible. The final setup looks like this. When the application starts drain the persistent buffer. This publishes messages from a previous connection outage and application exit. Whenever your app publishes a message, if connected publish with the:persistent flag. If not, add it to the persistent buffer. The producer can continue to "publish" during a connection outage. Configure channels to use auto recovery. This should cover all the bases.
There is always a trade off. Durability makes speed suffer since messages/queues/buffers are written to disk. However, if you're running a mission critical queue this a trade off you have to make. If you're just sending metrics or logs then it's not so important.
I recommend reading the error handling guide for the amqp gem. It covers things in more detail.
Thanks to Michael Klishin for reviewing an early draft of this post and all his hard work on the amqp and bunny gems.
Here is an example producer as described in this article. I recommend you refactor theBuffer class to take a redis connection as an argument. The key method should also be an argument. This makes the class more reusable. The code is here as a proof of concept.
require 'amqp'
require 'em-redis'
require 'multi_json'

class Buffer
  def initialize(connection, exchange)
    @connection, @exchange = connection, exchange
    @redis = EM::Protocols::Redis.connect
  end

  def publish(message, options = {})
    if connected?
      @exchange.publish message, options
    else
      @redis.rpush key, MultiJson.dump({message: message, options: options})
    end
  end

  def drain
    @redis.llen key do |size|
      @redis.lrange key, 0, size do |messages|
        messages.each do |msg|
          hash = MultiJson.load msg
          @exchange.publish hash.fetch('message'), hash.fetch('options')
        end
        @redis.del key
      end
    end
  end

  private
  def key
    'messages'
  end

  def connected?
    @connection.connected?
  end
end

AMQP.start do |connection|
  channel = AMQP::Channel.new connection
  channel.auto_recovery = true

  exchange = channel.direct 'buffer-test', durable: true

  buffer = RedisBuffer.new connection, exchange
  buffer.drain

  counter = 1

  EM.add_periodic_timer 1 do
    msg = "Message #{counter}"
    buffer.publish msg, persistent: true
    counter = counter + 1
  end

  show_stopper = proc do
    puts "Going down"
    connection.disconnect
    exit
  end

  connection.on_error do |ch, connection_close|
    raise connection_close.reply_text
  end

  connection.on_tcp_connection_loss do |conn, settings|
    conn.periodically_reconnect 2
  end

  connection.after_recovery do
    puts "Reconnected!"
    buffer.drain
  end

  trap 'INT', &show_stopper
  trap 'TERM', &show_stopper
end
Here is a durable consumer as well.
require 'amqp'

AMQP.start do |connection|
  connection.on_error do |ch, connection_close|
    raise connection_close.reply_text
  end

  connection.on_tcp_connection_loss do |conn, settings|
    conn.periodcially_reconnect 2
  end

  connection.after_recovery do
    puts "Reconnected!"
  end

  channel = AMQP::Channel.new connection
  channel.auto_recovery = true

  exchange = channel.direct 'buffer-test', durable: true
  queue = channel.queue(durable: true).bind(exchange)

  queue.subscribe do |headers, msg|
    puts msg
  end

  show_stopper = proc do
    puts "Going down"
    connection.disconnect
    exit
  end

  trap 'INT', &show_stopper
  trap 'TERM', &show_stopper
end
Start both of processes and experiment with killing them and the server at different times to see how things work.

Tuesday, 3 February 2015

How to Monitor RabbitMQ Server using Nagios check_rabbitmq Plugin


When you are running RabbitMQ server in production environment, it is essential to monitor RabbitMQ to make sure it is up and running properly, and all the messages in the RabbitMQ are getting processed properly.
If you are already using Nagios for your enterprise monitoring, you can monitor RabbitMQ using plugins.
nagios-plugins-rabbitmq is a Nagios plugin package that currently has 6 checks to monitor various aspects of RabbitMQ server.

This tutorial explains how to install, configure and monitor RabbitMQ Server using check_rabbitmq plugin.

1. Download check_rabbitmq Nagios Plugin

Download Nagios RabbitMQ plugin from here. Or, you can use wget to download it directly to your server as shown below.
cd ~
wget --no-check-certificate https://github.com/jamesc/nagios-plugins-rabbitmq/archive/master.zip
unzip nagios-plugins-rabbitmq-master.zip
After you unzip the download, it will create the nagios-plugins-rabbitmq-master directory. Rename this directory to nagios-plugins-rabbitmq (i.e Remove the “-master” from the directory name).
mv nagios-plugins-rabbitmq-master nagios-plugins-rabbitmq

2. Install Plugin in Libexec directory

Move this “nagios-plugins-rabbitmq” directory to nagios libexec directory where all the plugins are located. If you’ve installed Nagios from source, the location of libexec directory is /usr/local/nagios/libexec as shown below.
mv nagios-plugins-rabbitmq /usr/local/nagios/libexec
Also, make sure this plugin directory is owned by nagios user and group as shown below.
cd /usr/local/nagios/libexec/

chown -R nagios:nagios nagios-plugins-rabbitmq/
At this stage, if you test the nagios plugin by executing check_rabbitmq_server, you might get “Can’t locate Nagios/Plugin.pm in @INC” error message as shown below.
# cd /usr/local/nagios/libexec/nagios-plugins-rabbitmq/scripts

# ./check_rabbitmq_server
Can't locate Nagios/Plugin.pm in @INC (@INC contains: /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi /usr/lib/perl5/site_perl/5.8.7/i386-linux-thread-multi

3. Install Nagios::Plugin Perl Module

This RabbitMQ Server Nagios plugin requires the “Nagios::Plugin” perl package, which is a bunch of perl modules that is used for writing Nagios plugin in perl.
Install the Nagios::Plugin perl module as shown below. You can either install perl module from source, or from cpan shell command.
cd /usr/save

wget http://search.cpan.org/CPAN/authors/id/T/TO/TONVOON/Nagios-Plugin-0.36.tar.gz

tar xvfz Nagios-Plugin-0.36.tar.gz

cd Nagios-Plugin-0.36

perl Makefile.PL

make

make test

make install

4. Additional Perl Module Dependencies

In my case, I also needed the following Perl modules for the check_rabbitmq_server plugin to work properly.
Install the following Perl modules that are required from cpan shell as shown below:
cpan> install Math::Calc::Units
cpan> install Config::Tiny
cpan> install JSON
cpan> install Math::Calc::Units
After all the dependecies are installed, check_rabbitmq_server will not give any perl module error, instead you’ll see the following missing argument usage error message:
# ./check_rabbitmq_server
Usage: check_rabbitmq_server [options] -H hostname
Missing argument: hostname

5. Basic check_rabbitmq Usage

The following will connect to the RabbitMQ server that is running on dev-db server on port 15672, and return an OK message, with “Memory”, “Process”, “FD” and “Sockets” information of the connected RabbitMQ server as shown below.
# ./check_rabbitmq_server -H "dev-db" --port=15672
RABBITMQ_SERVER OK - Memory OK (1.16%) Process OK (0.02%) FD OK (2.93%) Sockets OK (0.12%) | Memory=1.16%;80;90 Process=0.02%;80;90 FD=2.93%;80;90 Sockets=0.12%;80;90
When the RabbitMQ Server is not up and running, you’ll get the following error message.
# ./check_rabbitmq_server  -H "dev-db" --port=15672
RABBITMQ_SERVER CRITICAL - Received 500 Can't connect to dev-db:15672 (connect: Connection refused) for path: nodes/rabbit@dev-db

6. Specify Username and Password

This plugin uses the RabbitMQ HTTP API that is used in the RabbitMQ management plugin.
In the previous example, the check_rabbitmq_server command uses the default username and password combination. i.e guest/guest. If you get the following “Access refused: nodes/rabbit@” error message, you’ll know that the default username/password combination (i.e guest/guest) is invalid.
# ./check_rabbitmq_server -H dev-db
RABBITMQ_SERVER UNKNOWN - Access refused: nodes/rabbit@dev-db
If you’ve changed the username and password for the RabbitMQ Management plugin, you should specify that particular username using -u and -p parameter as shown below.
The following example connects to the RabbitMQ server that is running on dev-db server on port 15672, using the username “guest”, and password “MySecretPassword”.
# ./check_rabbitmq_server -H "dev-db" --port=15672 -u "guest" -p "MySecretPassword"
RABBITMQ_SERVER OK - Memory OK (1.16%) Process OK (0.02%) FD OK (2.34%) Sockets OK (0.12%) | Memory=1.16%;80;90 Process=0.02%;80;90 FD=2.34%;80;90 Sockets=0.12%;80;90
Note: In all the following example, I did not pass the username/password, as it assumes that the default password is used. If you’ve changed it on your RabbitMQ server, make sure you pass -u and -p in all the following examples.

7. check_rabbitmq_overview Usage Example

Using check_rabbitmq_overview command you can monitor the following combination of values for critical and warning levels: 1) Total number of messages in the queue 2) Total number of messages that are ready 3) Total number of messages that are not acknowledged yet
If you don’t pass any critical or warning level, you’ll get an OK message, with the total messages, messages_ready and messages_unacknowledged as shown below.
# ./check_rabbitmq_overview  -H "dev-db" --port=15672
RABBITMQ_OVERVIEW OK - messages OK (229) messages_ready OK (229) messages_unacknowledged OK (0) | messages=229;; messages_ready=229;; messages_unacknowledged=0;;
This is an example of using check_rabbitmq_overview with critcal and warning levels. Since the following example returns the total number of messages larger than the critical limit of “100,10,10″, it returns the CRITICAL message.
# ./check_rabbitmq_overview -H "dev-db" --port=15672 -c 1000,10,10 -w 15,15,15
RABBITMQ_OVERVIEW CRITICAL - messages_ready CRITICAL (229), messages WARNING (229), messages_unacknowledged OK (0) | messages=229;15;1000 messages_ready=229;15;10 messages_unacknowledged=0;15;10
This is an example of using check_rabbitmq_overview with critcal and warning levels. Since the following example returns the total number of messages larger than the warning limit of “15,15,15″, it returns the WARNING message.
# ./check_rabbitmq_overview -H "dev-db" --port=15672 -c 1000,500,500 -w 15,15,15
RABBITMQ_OVERVIEW WARNING - messages WARNING (229) messages_ready WARNING (229), messages_unacknowledged OK (0) | messages=229;15;1000 messages_ready=229;15;500 messages_unacknowledged=0;15;500

8. check_rabbitmq_objects Usage Example

The following example will return the total number of object count for vhosts, exchange, bindings, queues and channels as shown below.
# ./check_rabbitmq_objects  -H "dev-db" --port=15672
RABBITMQ_OBJECTS OK - Gathered Object Counts | vhost=0;; exchange=8;; binding=3;; queue=1;; channel=0;;
Just like previous example, you can also pass warning and critical limits based on when you want to send the warning and critical alert for the total number of objects mentioned above.

9. check_rabbitmq_aliveness Usage Example

This will return whether the vhost defined in the RabbitMQ Server is alive or not. The following example returns an OK message for the aliveness check.
# ./check_rabbitmq_aliveness -H "dev-db" --port=15672
RABBITMQ_ALIVENESS OK - vhost: /
There is also a check_rabbitmq_watermark script that comes with this package, which displays mem_alarm and disk_free_alarm as shown below.
# ./check_rabbitmq_watermark  -H "dev-db" --port=15672
RABBITMQ_WATERMARK CRITICAL - mem_alarm disk_free_alarm

10. check_rabbitmq_queue Usage Example

This is helpful when you have several queues defined in your RabbitMQ instance, and you want to monitor a particular queue.
For example, the following monitors “DEV.Error.Read” queue specifically, and returns the messages, messages_ready, messages_unacknowledged,and consumers as shown below.
# ./check_rabbitmq_queue -H "dev-db" --port=15672 --queue="DEV.Error.Read"
RABBITMQ_QUEUE OK - messages OK (0) messages_ready OK (0) messages_unacknowledged OK (0) consumers OK (0) | messages=0;; messages_ready=0;; messages_unacknowledged=0;; consumers=0;;
Just like the previous examples, you can also set warning and critical limits using -w and -c as shown below. In the following example, it gives CRITICAL messages, as the total number of messages exceeded the critical limit of 200.
# ./check_rabbitmq_queue -H "dev-db" --port=15672 --queue="DEV.Status.Read" -w 100 -c 200
RABBITMQ_QUEUE CRITICAL - messages CRITICAL (229), messages_ready OK (229) messages_unacknowledged OK (0) consumers OK (0) | messages=229;100;200 messages_ready=229;; messages_unacknowledged=0;; consumers=0;;

11. Add check_rabbitmq_* Command Definitions

Append all the above check_rabbitmq_* commands to the commands.cfg file. This will setup all the proper check_rabbitmq_* command definitons that you can use in your Nagios service definitions.
The following examples shows that we’ve added the check_rabbitmq_server command definition to Nagios
# vi /usr/local/nagios/etc/objects/commands.cfg
define command {
 command_name check_rabbitmq_server
 command_line $USER1$/nagios-plugins-rabbitmq/scripts/check_rabbitmq_server -H $ARG1$ --port=$ARG2$ -u $ARG3$ -p $ARG4$
}
As you see above in all the examples, we were using the hostname (instead of ip-address). On my instance, instead of using “$HOSTADDRESS$” to get the ip-address, I’m passing the hostname as an argument to the command itself. If the $HOSTADDRESS$ works for you, change the $ARG1 to $HOSTADDRESS$, and adjust the other ARG numbers accordingly.

12. Create Nagios Service Definition for RabbitMQ Server

Once you’ve tested the Nagios RabbitMQ plugin from the command line, create a service definition like the following and place it under the /usr/local/nagios/etc/servers directory. In the following example, the RabbitMQ is running on the server called “dev-db”
# cat dev-db-server.cfg
define service {
    use                     generic-service
    host_name               dev-db
    service_description     RabbitMQ
    contacts                prodalert
    check_command           check_rabbitmq_server!dev-db!15672!guest!MySecretPassword
}
Restart the nagios after the above change. After this, anytime RabbitMQ Server goes down, Nagios will send an alert to the contacts defined in the “prodalert” object.

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...