Skip to main content

Vert.x Circuit Breaker

Vert.x Circuit Breaker is an implementation of the Circuit Breaker pattern for Vert.x.

It keeps track of the number of failures and open the circuit when a threshold is reached. Optionally, a fallback is executed.

Supported failures are:

  • failures reported by your code in a Future

  • exception thrown by your code

  • uncompleted futures (timeout)

Operations guarded by a circuit breaker are intended to by non-blocking and asynchronous in order to benefits from the Vert.x execution model.

Using the vert.x circuit breaker

To use the Vert.x Circuit Breaker, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-circuit-breaker</artifactId>
  <version>3.3.3</version>
</dependency>
  • Gradle (in your build.gradle file):

compile 'io.vertx:vertx-circuit-breaker:3.3.3'

Using the circuit breaker

To use the circuit breaker you need to:

  1. Create a circuit breaker, with the configuration you want (timeout, number of failure before opening the circuit)

  2. Execute some code using the breaker

Here is an example:

require 'vertx-circuit-breaker/circuit_breaker'
breaker = VertxCircuitBreaker::CircuitBreaker.create("my-circuit-breaker", vertx, {
  'maxFailures' => 5,
  'timeout' => 2000,
  'fallbackOnFailure' => true,
  'resetTimeout' => 10000
})

breaker.execute() { |future|
  # some code executing with the breaker
  # the code reports failures or success on the given future.
  # if this future is marked as failed, the breaker increased the
  # number of failures
}.set_handler() { |ar_err,ar|
  # Get the operation result.
}

The executed block receives a Future object as parameter, to denote the success or failure of the operation as well as the result. For example in the following example, the result is the output of a REST endpoint invocation:

require 'vertx-circuit-breaker/circuit_breaker'
breaker = VertxCircuitBreaker::CircuitBreaker.create("my-circuit-breaker", vertx, {
  'maxFailures' => 5,
  'timeout' => 2000
})

breaker.execute() { |future|
  vertx.create_http_client().get_now(8080, "localhost", "/") { |response|
    if (response.status_code() != 200)
      future.fail("HTTP error")
    else
      response.exception_handler(&future.method(:fail)).body_handler() { |buffer|
        future.complete(buffer.to_string())
      }
    end
  }
}.set_handler() { |ar_err,ar|
  # Do something with the result
}

The result of the operation is provided using the:

  • returned Future when calling execute methods

  • provided Future when calling the executeAndReport methods

Optionally, you can provide a fallback executed when the circuit is open:

require 'vertx-circuit-breaker/circuit_breaker'
breaker = VertxCircuitBreaker::CircuitBreaker.create("my-circuit-breaker", vertx, {
  'maxFailures' => 5,
  'timeout' => 2000
})

breaker.execute_with_fallback(lambda { |future|
  vertx.create_http_client().get_now(8080, "localhost", "/") { |response|
    if (response.status_code() != 200)
      future.fail("HTTP error")
    else
      response.exception_handler(&future.method(:fail)).body_handler() { |buffer|
        future.complete(buffer.to_string())
      }
    end
  }
}, lambda { |v|
  # Executed when the circuit is opened
  return "Hello"
}).set_handler() { |ar_err,ar|
  # Do something with the result
}

The fallback is called whenever the circuit is open, or if the isFallbackOnFailure is enabled. When a fallback is set, the result is using the output of the fallback function. The fallback function takes as parameter a Throwable object and returned an object of the expected type.

The fallback can also be set on the CircuitBreaker object directly:

require 'vertx-circuit-breaker/circuit_breaker'
breaker = VertxCircuitBreaker::CircuitBreaker.create("my-circuit-breaker", vertx, {
  'maxFailures' => 5,
  'timeout' => 2000
}).fallback(lambda { |v|
  # Executed when the circuit is opened.
  return "hello"
})

breaker.execute() { |future|
  vertx.create_http_client().get_now(8080, "localhost", "/") { |response|
    if (response.status_code() != 200)
      future.fail("HTTP error")
    else
      response.exception_handler(&future.method(:fail)).body_handler() { |buffer|
        future.complete(buffer.to_string())
      }
    end
  }
}

Callbacks

You can also configures callbacks invoked when the circuit is opened or closed:

require 'vertx-circuit-breaker/circuit_breaker'
breaker = VertxCircuitBreaker::CircuitBreaker.create("my-circuit-breaker", vertx, {
  'maxFailures' => 5,
  'timeout' => 2000
}).open_handler() { |v|
  puts "Circuit opened"
}.close_handler() { |v|
  puts "Circuit closed"
}

breaker.execute() { |future|
  vertx.create_http_client().get_now(8080, "localhost", "/") { |response|
    if (response.status_code() != 200)
      future.fail("HTTP error")
    else
      # Do something with the response
      future.complete()
    end
  }
}

You can also be notified when the circuit breaker decide to attempt to reset (half-open state). You can register such as callback with halfOpenHandler.

Event bus notification

Every time the circuit state changes, an event is published on the event bus. The address on which the event are sent is configurable with notificationAddress. If null is passed to this method, the notifications are disabled. By default, the used address is vertx.circuit-breaker.

Each event contains a Json Object with:

  • state : the new circuit breaker state (OPEN, CLOSED, HALF_OPEN)

  • name : the name of the circuit breaker

  • failures : the number of failures

  • node : the identifier of the node (local is Vert.x is not running in cluster mode)

The half-open state

When the circuit is “open,” calls to the circuit breaker fail immediately, without any attempt to execute the real operation. After a suitable amount of time (configured from resetTimeout, the circuit breaker decides that the operation has a chance of succeeding, so it goes into the half-open state. In this state, the next call to the circuit breaker is allowed to execute the dangerous operation. Should the call succeed, the circuit breaker resets and returns to the closed state, ready for more routine operation. If this trial call fails, however, the circuit breaker returns to the open state until another timeout elapses.