Created by Stan on 08-04-2023
In modern software systems, applications often rely on external services or APIs for various tasks. These dependencies can sometimes become the weakest link in your application, causing performance issues or complete failures if the external service experiences downtime. One way to protect your application from these issues is to implement the Circuit Breaker pattern. In this blog post, we'll discuss the Circuit Breaker pattern and demonstrate how to implement it in Ruby using code samples.
The Circuit Breaker pattern is a design pattern that helps improve the resilience of an application by monitoring the status of external services and preventing excessive resource consumption during failures. When the circuit breaker detects that a service is unavailable or experiencing high error rates, it "trips" or "opens" the circuit, preventing further requests to the failing service. After a specified period, the circuit breaker allows a limited number of requests to pass through to check if the service has recovered. If the service is still unavailable, the circuit remains open; otherwise, it returns to the "closed" state, allowing requests to flow normally.
Let's create a simple circuit breaker class in Ruby to demonstrate its implementation:
# circuit_breaker.rb class CircuitBreaker attr_reader :failure_threshold, :recovery_timeout def initialize(failure_threshold: 5, recovery_timeout: 60) @failure_threshold = failure_threshold @recovery_timeout = recovery_timeout @state = :closed @failure_count = 0 @last_failure_time = nil end def call return handle_open_circuit if open? begin result = yield reset_failure_count if closed? result rescue StandardError => e handle_failure(e) end end private def handle_failure(error) @failure_count += 1 @last_failure_time = Time.now if @failure_count >= failure_threshold open_circuit puts "Circuit is now open: #{error.message}" else raise error end end def handle_open_circuit if Time.now - @last_failure_time >= recovery_timeout begin result = yield close_circuit puts "Circuit is now closed" result rescue StandardError => e open_circuit puts "Circuit remains open: #{e.message}" end else puts "Circuit is open; request not allowed" end end def open? @state == :open end def closed? @state == :closed end def open_circuit @state = :open end def close_circuit @state = :closed reset_failure_count end def reset_failure_count @failure_count = 0 end end
In this example, we create a CircuitBreaker
class that takes two parameters: failure_threshold
and recovery_timeout
. The call
method wraps the code block and manages the circuit state. If the circuit is closed and the wrapped code raises an exception, the failure count increases. When the failure count reaches the threshold, the circuit opens. In the open state, the circuit breaker prevents further requests and checks the recovery timeout. After the recovery timeout, it allows a limited number of requests to pass through to test the service availability.
Now let's see how to use the CircuitBreaker
class in a Ruby application. Imagine we have a simple client that fetches data from an external API:
# api_client.rb require 'net/http' class ApiClient def fetch_data(endpoint) uri = URI(endpoint) response = Net::HTTP.get_response(uri) if response.is_a?(Net::HTTPSuccess) JSON.parse(response.body) else raise "API request failed with status: #{response.code}" end end end
We can now use the CircuitBreaker
class to protect our application from failures in the ApiClient
:
# app.rb require_relative 'circuit_breaker' require_relative 'api_client' circuit_breaker = CircuitBreaker.new(failure_threshold: 3, recovery_timeout: 30) api_client = ApiClient.new endpoint = "https://api.example.com/data" 10.times do circuit_breaker.call do begin data = api_client.fetch_data(endpoint) puts "Data fetched successfully: #{data}" rescue => e puts "Error: #{e.message}" end end sleep(5) end
In this example, we create an instance of the CircuitBreaker
with a failure threshold of 3 and a recovery timeout of 30 seconds. We then make 10 requests to the external API using the ApiClient
wrapped in the CircuitBreaker
. If the API experiences issues, the circuit breaker will open after three consecutive failures, preventing further requests for 30 seconds.
The simple circuit breaker implementation shown in this article can be extended with additional features, such as monitoring, logging, or exponential backoff for recovery. Alternatively, you can use existing Ruby libraries that implement the circuit breaker pattern, such as Yammer's circuitbox
or Shopify's semian
gems.
The Circuit Breaker pattern is an essential tool for building resilient and fault-tolerant applications, especially when dealing with external services or APIs. In this article, we explored the concept of the Circuit Breaker pattern and demonstrated how to implement a basic version in Ruby. By incorporating this pattern into your Ruby applications, you can better manage failures and prevent cascading issues that could impact your application's performance and user experience. As you continue to develop your applications, consider adopting the Circuit Breaker pattern to enhance your application's resilience and stability.
Architecture
Posted on 07 Apr, 2023Architecture
Posted on 08 Apr, 2023Architecture
Posted on 10 Apr, 2023