”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > Go 中的断路器:阻止级联故障

Go 中的断路器:阻止级联故障

发布于2024-08-06
浏览:929

Circuit Breakers in Go: Stop Cascading Failures

Circuit Breakers

A circuit breaker detects failures and encapsulate the logic of handling those failures in a way that prevents the failure from constantly recurring. For example, they’re useful when dealing with network calls to external services, databases, or really, any part of your system that might fail temporarily. By using a circuit breaker, you can prevent cascading failures, manage temporary errors, and maintain a stable and responsive system amidst a system breakdown.

Cascading Failures

Cascading failures occur when a failure in one part of the system triggers failures in other parts, leading to widespread disruption. An example is when a microservice in a distributed system becomes unresponsive, causing dependent services to timeout and eventually fail. Depending on the scale of the application, the impact of these failures can be catastrophic which is going to degrade performance and probably even impact user experience.

Circuit Breaker Patterns

A circuit breaker itself is a technique/pattern and there are three different states it operates which we will talk about:

  1. Closed State: In a closed state, the circuit breaker allows all requests to pass through to the target service normally as they would. If the requests are successful, the circuit remains closed. However, if a certain threshold of failures is reached, the circuit transitions to the open state. Think of it like a fully operational service where users can log in and access data without issues. Everything is running smoothly.

Circuit Breakers in Go: Stop Cascading Failures

2. Open State : In an open state, the circuit breaker immediately fails all incoming requests without attempting to contact the target service. The state is entered to prevent further overload of the failing service and give it time to recover. After a predefined timeout, the circuit breaker moves to the half-open state. A relatable example is this; Imagine an online store experiences a sudden issue where every purchase attempt fails. To avoid overwhelming the system, the store temporarily stops accepting any new purchase requests.

Circuit Breakers in Go: Stop Cascading Failures

3. Half-Open State : In the half-open state, the circuit breaker allows a (configurable) limited number of test requests to pass through to the target service. And if these requests are successful, the circuit transitions back to the closed state. If they fail, the circuit returns to the open state. In the example of the online store i gave in the open state above, this is where the online store starts to allow a few purchase attempts to see if the issue has been fixed. If these few attempts succeed, the store will fully reopen its service to accept new purchase requests.

This diagram shows when the circuit breaker tries to see if requests to Service B are successful and then it fails/breaks:

Circuit Breakers in Go: Stop Cascading Failures

The follow up diagram then shows when the test requests to Service B succeeds, the circuit is closed, and all further calls are routed to Service B again:

Circuit Breakers in Go: Stop Cascading Failures

Note : Key configurations for a circuit breaker include the failure threshold (number of failures needed to open the circuit), the timeout for the open state, and the number of test requests in the half-open state.

Implementing Circuit Breakers in Go

It’s important to mention that prior knowledge of Go is required to follow along in this article.

As with any software engineering pattern, circuit breakers can be implemented in various languages. However, this article will focus on implementation in Golang. While there are several libraries available for this purpose, such as goresilience, go-resiliency, and gobreaker, we will specifically concentrate on using the gobreaker library.

Pro Tip : You can see the internal implementation of the gobreaker package, check here.

Let’s consider a simple Golang application where a circuit breaker is implemented to handle calls to an external API. This basic example demonstrates how to wrap an external API call with the circuit breaker technique:

Let’s touch on a few important things:

  1. gobreaker.NewCircuitBreaker function initializes the circuit breaker with our custom settings
  2. cb.Execute method wraps the HTTP request, automatically managing the circuit state.
  3. MaximumRequests is the maximum number of requests allowed to pass through when the state is half-open
  4. Interval is the cyclic period of the closed state for the circuit breaker to clear the internal counts
  5. Timeout is the duration before transitioning from open to half-open state.
  6. ReadyToTrip is called with a copy of counts whenever a request fails in the closed state. If ReadyToTrip returns true, the circuit breaker will be placed into the open state. In our case here, it returns true if requests have failed more then three consecutive times.
  7. OnStateChange is called whenever the state of the circuit breaker changes. You would usually want to collect the metrics of the state change here and report to any metrics collector of your choice.

Let’s write some unit tests to verify our circuit breaker implementation. I will only be explaining the most critical unit tests to understand. You can check here for the full code.

  1. We will write a test that simulates consecutive failed requests and checks if the circuit breaker trips to the open state. Essentially, after 3 failures, when the forth failure occurs, we expect the circuit breaker to trip (open) since our condition says counts.ConsecutiveFailures > 3 . Here's what the test looks like:
 t.Run("FailedRequests", func(t *testing.T) {
         // Override callExternalAPI to simulate failure
         callExternalAPI = func() (int, error) {
             return 0, errors.New("simulated failure")
         }

         for i := 0; i 



  1. We will test the open > half - open > closed states. But we will first simulate an open circuit and call a timeout. After a timeout, we need to make at least one success request for the circuit to transition to half-open. After the half-open state, we need to make another success request for the circuit to be fully closed again. If for any reason, there’s no record of a success request in the case, it will go back to being open. Here’s how the test looks like:
 //Simulates the circuit breaker being open, 
 //wait for the defined timeout, 
 //then check if it closes again after a successful request.
     t.Run("RetryAfterTimeout", func(t *testing.T) {
         // Simulate circuit breaker opening
         callExternalAPI = func() (int, error) {
             return 0, errors.New("simulated failure")
         }

         for i := 0; i 



  1. Let’s test the ReadyToTrip condition which triggers after 2 consecutive failure requests. We'll have a variable that tracks for consecutive failures. The ReadyToTrip callback is updated to check if the circuit breaker trips after 2 failures ( counts.ConsecutiveFailures > 2). We will write a test that simulates failures and verifies the count and that the circuit breaker transitions to the open state after the specified number of failures.
 t.Run("ReadyToTrip", func(t *testing.T) {
         failures := 0
         settings.ReadyToTrip = func(counts gobreaker.Counts) bool {
             failures = int(counts.ConsecutiveFailures)
             return counts.ConsecutiveFailures > 2 // Trip after 2 failures
         }

         cb = gobreaker.NewCircuitBreaker(settings)

         // Simulate failures
         callExternalAPI = func() (int, error) {
             return 0, errors.New("simulated failure")
         }
         for i := 0; i 



Advanced Strategies

We can take it a step further by adding an exponential backoff strategy to our circuit breaker implementation. We will this article keep it simple and concise by demonstrating an example of the exponential backoff strategy. However, there are other advanced strategies for circuit breakers worth mentioning, such as load shedding, bulkheading, fallback mechanisms, context and cancellation. These strategies basically enhance the robustness and functionality of circuit breakers. Here’s an example of using the exponential backoff strategy:

Exponential Backoff

Circuit breaker with exponential backoff

Let’s make a couple of things clear:

Custom Backoff Function: The exponentialBackoff function implements an exponential backoff strategy with a jitter. It basically calculates the backoff time based on the number of attempts, ensuring that the delay increases exponentially with each retry attempt.

Handling Retries: As you can see in the /api handler, the logic now includes a loop that attempts to call the external API up to a specified number of attempts ( attempts := 5). After each failed attempt, we wait for a duration determined by the exponentialBackoff function before retrying.

Circuit Breaker Execution: The circuit breaker is used within the loop. If the external API call succeeds ( err == nil), the loop breaks, and the successful result is returned. If all attempts fail, an HTTP 503 (Service Unavailable) error is returned.

Integrating custom backoff strategy in a circuit breaker implementation indeed aims to handle transient errors more gracefully. The increasing delays between retries help reduce the load on failing services, allowing them time to recover. As evident in our code above, our exponentialBackoff function was introduced to add delays between retries when calling an external API.

Additionally, we can integrate metrics and logging to monitor circuit breaker state changes using tools like Prometheus for real-time monitoring and alerting. Here’s a simple example:

Implementing a circuit breaker pattern with advanced strategies in go

As you’ll see, we have now done the following:

  1. In L16–21, we define a prometheus counter vector to keep track of the number of requests and their state (success, failure, circuit breaker state changes).
  2. In L25–26, the metrics defined are registered with Prometheus in the init function.

Pro Tip : The init function in Go is used to initialize the state of a package before the main function or any other code in the package is executed. In this case, the init function registers the requestCount metric with Prometheus. And this essentially ensures that Prometheus is aware of this metric and can start collect data as soon as the application starts running.

  1. We create the circuit breaker with custom settings, including the ReadyToTrip function that increases the failure counter and determines when to trip the circuit.

  2. OnStateChange to log state changes and increment the corresponding prometheus metric

  3. We expose the Prometheus metrics at /metrics endpoint

Wrapping Up

To wrap up this article, i hope you saw how circuit breakers play a huge role in building resilient and reliable systems. By proactively preventing cascading failures, they fortify the reliability of microservices and distributed systems, ensuring a seamless user experience even in the face of adversity.

Keep in mind, any system designed for scalability must incorporate strategies to gracefully handle failures and swiftly recover —  Oluwafemi , 2024

Originally published at https://oluwafemiakinde.dev on June 7, 2024.

版本声明 本文转载于:https://dev.to/oluwafemiakind1/circuit-breakers-in-go-stop-cascading-failures-3p1l?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    [2明确担心Microsoft Visual C(MSVC)在正确实现两相模板实例化方面努力努力。该机制的哪些具体方面无法按预期运行?背景:说明:的初始Syntax检查在范围中受到限制。它未能检查是否存在声明名称的存在,导致名称缺乏正确的声明时会导致编译问题。为了说明这一点,请考虑以下示例:一个符合...
    编程 发布于2025-02-06
  • 'exec()
    'exec()
    Exec对本地变量的影响: exec function,python staple,用于动态代码执行的python staple,提出一个有趣的Query:它可以在函数中更新局部变量吗? python 3 Dialemma 在Python 3中,以下代码shippet无法更新本地变量,因为人们...
    编程 发布于2025-02-06
  • 如何使用PHP从XML文件中有效地检索属性值?
    如何使用PHP从XML文件中有效地检索属性值?
    从php 您的目标可能是检索“ varnum”属性值,其中提取数据的传统方法可能会使您感到困惑。 - > attributes()为$ attributeName => $ attributeValue){ echo $ attributeName,'=“',$ at...
    编程 发布于2025-02-06
  • 如何使用不同数量列的联合数据库表?
    如何使用不同数量列的联合数据库表?
    合并列数不同的表 当尝试合并列数不同的数据库表时,可能会遇到挑战。一种直接的方法是在列数较少的表中,为缺失的列追加空值。 例如,考虑两个表,表 A 和表 B,其中表 A 的列数多于表 B。为了合并这些表,同时处理表 B 中缺失的列,请按照以下步骤操作: 确定表 B 中缺失的列,并将它们添加到表的末...
    编程 发布于2025-02-06
  • 如何使用Python的记录模块实现自定义处理?
    如何使用Python的记录模块实现自定义处理?
    使用Python的Loggging Module 确保正确处理和登录对于疑虑和维护的稳定性至关重要Python应用程序。尽管手动捕获和记录异常是一种可行的方法,但它可能乏味且容易出错。解决此问题,Python允许您覆盖默认的异常处理机制,并将其重定向为登录模块。这提供了一种方便而系统的方法来捕获和...
    编程 发布于2025-02-06
  • 如何从Google API中检索最新的jQuery库?
    如何从Google API中检索最新的jQuery库?
    从Google APIS 问题中提供的jQuery URL是版本1.2.6。对于检索最新版本,以前有一种使用特定版本号的替代方法,它是使用以下语法: https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js(google hosted...
    编程 发布于2025-02-06
  • 为什么使用Firefox后退按钮时JavaScript执行停止?
    为什么使用Firefox后退按钮时JavaScript执行停止?
    导航历史记录问题:JavaScript使用Firefox Back Back 此行为是由浏览器缓存JavaScript资源引起的。 To resolve this issue and ensure scripts execute on subsequent page visits, Firefox...
    编程 发布于2025-02-06
  • 如何检索SQL中每个用户的最新登录日期?
    如何检索SQL中每个用户的最新登录日期?
    SQL查询每个用户的最后登录记录日期 问题: 假设数据库表包含用户登录信息,我们如何检索每个用户的最后登录日期?考虑到可能存在重复的登录日期。 解决方案: 解决此问题有两种常见方法: 方法一:使用INNER JOIN的子查询 此方法利用子查询来识别每个用户的最后登录日期,然后用于过滤主表: sel...
    编程 发布于2025-02-06
  • 如何有效处理Java中的时区转换?
    如何有效处理Java中的时区转换?
    跨时区的时区转换 java.util.date类缺乏明确的时区分配。它的Tostring方法误导了JVM的默认时区,导致混乱。为了避免这些陷阱,现代Java利用Java.Time软件包进行更健壮的时间处理。 在Java.Time中转换时区,指定一个时区并调用ZonedDateTime.now方法...
    编程 发布于2025-02-06
  • 为什么我的烧瓶应用程序会抛出“ templatenotfound”错误,即使我的模板文件存在?
    为什么我的烧瓶应用程序会抛出“ templatenotfound”错误,即使我的模板文件存在?
    故障排除flask的templatenotfound错误 在尝试在flask中渲染模板,例如'home.html',您可能会遇到''''',您可能会遇到''' jinja2.exceptions.templatenot...
    编程 发布于2025-02-06
  • 删除地图条目是否会导致持有指针时的内存泄漏?
    删除地图条目是否会导致持有指针时的内存泄漏?
    [2一片指针可能会导致内存泄漏。这个问题将查询扩展到地图,特别是从持有的地图中删除条目是否会导致相似的行为。检查实现:确定此说法的真实性,让我们调查GO的运行时:Runtime/Hashmap.go(function MapDelete())中的映射删除的源代码。检查表明,删除(#600,#602)...
    编程 发布于2025-02-06
  • 对象拟合:IE和Edge中的封面失败,如何修复?
    对象拟合:IE和Edge中的封面失败,如何修复?
    解决此问题,我们采用了一个巧妙的CSS解决方案来解决问题:高度:100%; 高度:auto; 宽度:100%; //对于水平块 ,使用绝对定位将图像定位在中心,以object-fit:object-fit:cover in IE和edge消除了问题。现在,图像将按比例扩展,保持所需的效果而不会失...
    编程 发布于2025-02-06
  • OpenGL的屏幕外渲染如何工作?
    OpenGL的屏幕外渲染如何工作?
    在OpenGl中roendering:详细的指南 framebuffers:读取像素: glreadpixels允许我们检索存储在flamebuffer中的像素数据并将其传输到主内存中。一种基本的OffScreen渲染方法涉及以下步骤:创建FrameBuffer: 使用glgenframebuff...
    编程 发布于2025-02-06
  • 如何使用PHP将斑点(图像)正确插入MySQL?
    如何使用PHP将斑点(图像)正确插入MySQL?
    在尝试将image存储在mysql数据库中时,您可能会遇到一个可能会遇到问题。本指南将提供成功存储您的图像数据的解决方案。 easudy values('$ this-> image_id','file_get_contents($ tmp_image)...
    编程 发布于2025-02-06
  • 为什么我的Mac OS X MySQL Server在不更新PID文件的情况下退出?
    为什么我的Mac OS X MySQL Server在不更新PID文件的情况下退出?
    mySQL启动错误:“服务器退出而无需更新PID文件”在Mac X 退出服务器,而无需更新PID文件此问题通常源于权限问题。以下是解决它的方法:检查运行mysql Instances 如果找到一个实例,请终止它: 接下来,检查/usr/usr/local/local/var/mysql/dire...
    编程 发布于2025-02-06

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3