Go中的软件架构:断路器,可靠性的云设计模式

200 阅读3分钟

Go中的软件架构。断路器,可靠性的云设计模式

欢迎来到涵盖质量属性/非功能要求的系列的另一个帖子部分,这次我说的是一个提高可靠性的云设计模式,叫做。断路器


什么是 "断路器"?

Circuit Breaker 是一种模式,它可以防止执行可能失败的操作*,并* 跟踪失败的情况,以避免浪费资源。例如,在微服务架构中,这种模式在处理远程资源时非常有用,特别是在这些服务不在边界范围内的情况下,因此可能由不同的团队维护。

以下面的例子为例,我们有一个我们维护的服务,叫做服务A,我们的服务依赖于两个外部的服务。服务1服务2,我们使用这些服务来增强数据,我们假设服务1失败了,我们应该失败吗?我们是否决定这样做,取决于业务逻辑和我们从该服务获取的数据。在这种情况下,我们的需求表明,虽然不是很理想,但在服务1发生故障时继续进行还是可以的,然而这带来了另一个问题:我们如何确定何时回到 服务1并再次开始从它那里请求数据?

Circuit Breaker

这时,Circuit Breaker 模式就出现了,因为它不仅允许我们检测故障,还可以在一段时间内跟踪这些故障,以确定何时是重试的最佳时机。这种模式通常被实现为一个状态机,定义了三种不同的状态。

  • 关闭。
  • 开放,和
  • 半开

根据不同的实现方式,你可能会看到其他属性或状态的添加,但这是大多数时候的实现方式。

Circuit Breaker Implementation

我们如何在Go中实现电路模式?

Go中的Circuit Braker ,有几种不同的实现方式,在这个例子中我使用了 mercari/go-circuitbreaker因为在我看来,它是提供更多灵活性和选项的包之一,而且它与上下文包很好地结合在一起。

这个例子的具体实现使用了我们为To-Do微服务定义的Elasticsearch Repository,我更新了它以增加对Circuit Breaker 的支持。

circuitbreaker.New(&circuitbreaker.Options{
			ShouldTrip:  circuitbreaker.NewTripFuncConsecutiveFailures(3),
			OpenTimeout: time.Minute * 2,
			OnStateChange: func(oldState, newState circuitbreaker.State) {
				logger.Info("state changed",
					zap.String("old", string(oldState)),
					zap.String("new", string(newState)),
				)
			},
		}

上面的配置表明,它应该在连续3次失败后跳闸(或打开),并且在再次重试前应该让它开放2分钟。使用它的方法具体做了以下工作。

// By searches Tasks matching the received values.
func (t *Task) By(ctx context.Context, args internal.SearchArgs) (_ internal.SearchResults, err error) {
	// XXX: Instrumentation ommited for brevity
	if !t.cb.Ready() {
		return internal.SearchResults{}, internal.NewErrorf(internal.ErrorCodeUnknown, "service not available")
	}

	defer func() {
		err = t.cb.Done(ctx, err)
	}()

	res, err := t.search.Search(ctx, args)
	// XXX: Error handling ommitted for brevity

	return res, nil
}

这个变化的关键部分是,我们正在。

  1. 检查Circuit Breaker 是否已打开。
  2. 在调用它之后更新Circuit Breaker 的状态

什么时候调用它取决于我们在上面实例化circuitbreaker 类型时定义的配置。

总结

Circuit Breaker 是一个云设计模式,旨在提高我们服务的可靠性,当我们依赖于我们不一定要维护的服务时,所以如果需要连接到一个不同的服务,也许那个服务正在失败,也许我们不应该在它连续失败一段时间后调用它,但同时我们也需要一种方法来重试,以防它恢复正常。Circuit Breaker 模式处理了所有这些复杂性,并允许我们定义这些规则,这样我们在试图与外部服务进行交互时就不会浪费资源,更重要的是它改善了我们用户的体验,因为现在我们的服务可以尽快返回数据了。