The microservices pattern is pretty standard for today's software architecture. Microservices let you break up your application into small chunks and avoid having one giant monolith. The only problem is that if one of these services fails, it could have a cascading effect on your whole architecture.

Luckily, there is another pattern that can help with this issue: The circuit breaker pattern.

This article explains what a circuit breaker is and how to use the pattern in your Node.js applications. We'll use Opossum, a Node.js implementation of the circuit breaker pattern.

What is a circuit breaker?

Before we dive into an example, let's quickly define a circuit breaker and how to use the pattern in your code.

You might already be familiar with circuit breakers if you’ve tried to run too many household appliances at once. The lights go out due to an overwhelming influx of electricity. To restore power, you need to go down to the basement, find the electrical box, search for the breaker that “tripped,” and reset it. Circuit breakers protect your residence by shutting down during an electrical surge.

The circuit breaker pattern works similarly when dealing with microservices that communicate over a network. Its purpose is to reduce the impact of a service that is running too slowly or that can’t be reached due to a network failure. The circuit breaker monitors for such failures. Once failures reach a particular threshold, the circuit “trips,” and any call made after that either returns an error or adopts a fallback response. Then, after a set time has passed, the circuit breaker makes test calls to the affected services. If the calls are successful, the circuit closes, and traffic starts flowing again.

Circuit breakers are especially important when multiple services depend on each other. If one service fails, it could take down the whole architecture. Remember the first Death Star explosion in the Star Wars movie series? A good circuit breaker might have avoided that.

What is Opossum?

Opossum is a circuit breaker for Node.js. When things start to fail, opossum plays dead and fails fast. If you want to, you can provide a fallback function to be executed when in the failure state.

Opossum has been a community project since late 2016, and it now has more than 70,000 downloads per week. It is supported by the Nodeshift community. Recently, Red Hat has released a fully supported version of Opossum that is distributed through Red Hat’s customer registry as @redhat/opossum. Opossum will always be a community project, but if you would like to know that the version you are using has Red Hat's support, then the @redhat/opossum version might be for you. You can learn more about Red Hat's Node.js offerings here.

The following sections show how to add this module to an application and how to use it to protect your microservices.

Adding Red Hat Opossum to your application

Adding the @redhat/opossum module to your application is just like adding any other Node.js module, with one small change. Because you will download this module from the Red Hat customer registry, you need to tell npm to download any modules with the @redhat namespace from the Red Hat registry while continuing to download all other modules from the upstream NPM registry.

To start, add a .npmrc file in your application's root directory. The file should look something like this:

@redhat:registry=https://npm.registry.redhat.com
registry=https://registry.npmjs.org

With this file in place, you can run the following command successfully:

$ npm install @redhat/opossum

To require the module in your application, insert the same kind of statement you would for every other Node.js module:

const CircuitBreaker = require(‘@redhat/opossum’)

Now, let's take a look at an example.

Example: Opossum circuit breaker for Node.js

For this example, we are going to use the Nodeshift Circuit Breaker Starter Application.

Note: This example works the same way on both the community and Red Hat versions of Opossum.

The example consists of two simple Node.js microservices, so let's look at them both.

The greeting service

The greeting-service is the application's entry point. A simple web page makes a call to the greeting REST endpoint. This endpoint then makes a call, wrapped in a circuit breaker, to the second service. The web page also has a button to let you toggle the name service (which I will introduce shortly) on or off to simulate a network failure.

Here's the code responsible for the greeting service:

...
// We require Opossum
const Opossum = require('@redhat/opossum');
…

// Set some circuit breaker options
const circuitOptions = {
  timeout: 3000, // If name service takes longer than .3 seconds, trigger a failure
  errorThresholdPercentage: 50, // When 50% of requests fail, trip the circuit
  resetTimeout: 10000 // After 10 seconds, try again.
};
…

// Use a circuit breaker for the name service and define fallback function
const circuit = new Opossum(nameService, circuitOptions);
circuit.fallback(_ => 'Fallback');

…

// Greeting API
app.get('/api/greeting', (request, response) => {
 // Using the Circuits fire method to execute the call to the name service
  circuit.fire(`${nameServiceHost}/api/name`).then(name => {
    response.send({ content: `Hello, ${name}`, time: new Date() });
  }).catch(console.error);
});

Next, we pass the nameService function as the first argument to the circuit breaker. It looks something like the following, which is a standard call to another endpoint using axios:

'use strict';
const axios = require('axios');

module.exports = endpoint => {
  return new Promise((resolve, reject) => {
    axios.get(endpoint)
      .then(response => {
        if (response.status !== 200) {
          return reject(new Error(`Expected status code 200, instead got ${response.status}`));
        }

        resolve(response.data);
      })
      .catch(reject);
  });
};

The name service

The other microservice, name-service, is a REST endpoint that sends back a response based on the on or off toggle I mentioned before.

Starting the application is straightforward. From the repository's root directory, run the ./start-localhost.sh file to bring up the two Node.js processes. The script will also attempt to open a web browser to the location of the running application.

Pressing the invoke button on the web page contacts the first endpoint. The endpoint sends back a response saying whether it could contact the second service or had to use the fallback. You can click the toggle button to simulate a network failure.

Conclusion

This article has shown how a circuit breaker helps to reduce unexpected failures in microservices. You can use the @redhat/opossum module to add this pattern to your Node.js applications. To find out more about this new supported offering, check out the article Opossum: Fully supported circuit breaker module for Red Hat build of Node.js on the Red Hat Customer Portal.

See these resources to learn more about the topics discussed in this article:

Last updated: February 11, 2024