Fork me on GitHub

This tutorial describes how to use Drools and the Gnostic service for rule based SLA Management. The tutorial is composed of the following sections:

Introduction

As introduced in the Watches & SLAs document, Rio provides mechanisms to collect and analyze programmer defined metrics defined in a distributed application.

Using this support, services can have SLAs attached to them, ensuring operational criteria is met. The dynamic service's operational behavior can be monitored to dynamically scale it's environment up & down based on declarative attributes, be relocated, or trigger specific response(s) based on observed stimulus.

The diagram below shows the architecture used to provide the support for observed system and application defined metrics. The approach is based on a sensor-effector pattern. Data is observed from applications, OS, hardware, etc ... and feed into SLA Management and Policy Enforcement.

Where the SLA Rule approach fits in is in Policy Enforcement. Rio provides a service called Gnostic, that provides a distributed point that coalesces metrics collected by services to a Complex Event Processing (CEP) engine. The CEP that the Gnostic service currently provides support for is Drools Fusion.

Why Gnostic? Gnostic comes from one of the Greek words for knowledge, or Gnosis

The CEP provides a way to add events collected from observed system and application defined metrics, and accumulate temporal knowledge that rules are then executed on. Rule execution results in actions taken based on the conditions that are met.

A rule is structured as follows:

rule "name"

when
    <conditions>
then
    <actions>;

The rule structure above specifies that when a particular set of conditions occur, specified in the Left Hand Side (LHS), then do what is specified as a list of actions in the Right Hand Side (RHS).

An example of a rule (taken from the Hospital example) follows:

declare CalculablePatient
    @role(event)
    @timestamp(date)
end

rule "Patient Alert Rule"
when
    $p : CalculablePatient(id == "pulse", value < 50) from entry-point "calculables-stream"
then
    Patient p = $p.getPatient();
    Doctor d = p.getDoctor();
    d.patientAlert(p);
end

In the rule above, every patient has a pulse. There is a "pulse" watch created that monitors each patient's pulse. The CalculablePatient event contains the pulse value, and the Patient object. If the pulse drops below 50, the patient's Doctor is notified.

The LHS deals with determining if a patient's pulse drops below 50. If this condition is met, the RHS executes, notifying the Doctor.

Note that this is not a tutorial for writing Drools rules. Please refer to available documentation for Drools Expert and Drools Fusion.

So why do this using a rule and not programmatically? Good question :)

  • Previous versions of Rio provided SLA Policy Handlers that have been developed empirically, exist in code, and are wired directly to watches. Using a rules approach we can add declarative rules to the system offering a wider range of functionality and behavior.
  • The SLA Rules approach separates data and logic in a cleaner way. The logic is in the rules, can be added to a running system easily, changed, removed, etc ...

How Gnostic fits into the system

The Gnostic is a service that is dynamically deployed, just like any other service you need to have running. The Gnostic is configured as part of your opstring (deployment) configuration, and you wire up the Gnostic with service feeds, that direct metrics from the service to Gnostic, where the events are added to the Gnostic's CEP as temporal data.

Once the rule executes, the RHS can perform actions on services or ask Rio to increment or decrement service instances, relocate services, deploy additional services, or act directly on your services.

Each rule has a global org.rioproject.gnostic.service.DeployedServiceContext set, providing context on deployed services. The DeployedServiceContext class provides the support for the RHS of the rule to interface with Rio, and/or obtain a proxy for an associated service enabling the RHS to invoke methods on specific service instances.

The example below is from the Hospital example. It shows a rule that checks if there are enough Beds in the Hospital. If the number of Beds is 0, the DeployedServiceContext is used to increment (scale), the number of available Beds.

global org.rioproject.gnostic.service.DeployedServiceContext context;

declare Calculable
    @role(event)
    @timestamp(date)
end

rule "Available Bed Rule"
when
    $beds : Calculable(id == "availableBeds", value == 0) from entry-point "calculables-stream"
then
    context.increment("Beds", "Hospital");
end

Configuring Gnostic

An extension to the Rio DSL allows the inline declaration of rules and the required attributes needed to load, create and provide the necessary inputs for the rule(s) to operate.

The syntax diagram for the rules declaration is shown here:




The rules declaration results in the creation of a Gnostic service configured with rule elements. Each rule has a:

  • resource
    The name of the rule file (resource). If the resource name does not include an extension, the .drl extension is added. The resource name may also include either file: or http. If the resource name starts with either of these prefixes, the appropriate file or URL resource will be created. If the resource does not start with file: or http, the resource will be loaded as a classpath resource.
  • ruleClassPath
    The ruleClassPath is added to a rule declaration if the rule has domain specific requirements that include either rule resources (see above), model classes that interact with the rule, or require interactions with (domain specific) services. The ruleClassPath is either an artifact (groupId:artifactId:version), or a comma separated list of jars.
  • serviceFeed
    The serviceFeed declares the name of the service (and optionally the opstring name) to associate to. Once bound, the Gnostic registers for notifications of Calculables being added to the service's WatchDataSource as a WatchDataReplicator for each named watch. Watch names are provided as a comma separated list.

An example follows:

rules {
    rule {
        resource 'DoctorRule, PatientAlert, AvailableBedRule'
        ruleClassPath 'org.rioproject.examples.hospital:hospital-rule:2.0'
        serviceFeed(name: "Doctors") {
            watches "numPatients"
        }
        serviceFeed(name: "Beds") {
            watches "pulse"
        }
        serviceFeed(name: "Admission") {
            watches "availableBeds"
        }
    }
}

Feeds from system watches

For declaring serviceFeed watch names that are to be bound to system oriented watches like JVM Memory, CPU utilization, the following constants can be used:

Constant Explanation
SystemWatchID.SYSTEM_CPU A watch that observes CPU utilization for a compute resource.
SystemWatchID.PROC_CPU A watch that observes CPU utilization for the process (JVM) the service is running in.
SystemWatchID.DISK_SPACE A watch that observes diskspace utilization for a compute resource.
SystemWatchID.SYSTEM_MEMORY A watch that observes system memory utilization for a compute resource.
SystemWatchID.JVM_MEMORY A watch that observes memory utilization for the process (JVM) the service is running in.
SystemWatchID.JVM_PERM_GEN A watch that observes memory the permanent generation utilization for the process (JVM) the service is running in.

Back to top

Version: 5.6. Last Published: 2017-01-01.