您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Go语言

图解 Go 微服务中的熔断器和重试

时间:2020-06-14 15:38:16  来源:  作者:

今天我们来讨论微服务架构中的自我恢复能力。通常情况下,服务间会通过同步或异步的方式进行通信。我们假定把一个庞大的系统分解成一个个的小块能将各个服务解耦。管理服务内部的通信可能有点困难了。你可能听说过这两个著名的概念:熔断和重试。

熔断器

图解 Go 微服务中的熔断器和重试

 

01

想象一个简单的场景:用户发出的请求访问服务 A 随后访问另一个服务 B。我们可以称 B 是 A 的依赖服务或下游服务。到服务 B 的请求在到达各个实例前会先通过负载均衡器。

后端服务发生系统错误的原因有很多,例如慢查询、network blip 和内存争用。在这种场景下,如果返回 A 的 response 是 timeout 和 server error,我们的用户会再试一次。在混乱的局面中我们怎样来保护下游服务呢?

图解 Go 微服务中的熔断器和重试

 

02

熔断器可以让我们对失败率和资源有更好的控制。熔断器的设计思路是不等待 TCP 的连接 timeout 快速且优雅地处理 error。这种 fail fast 机制会保护下游的那一层。这种机制最重要的部分就是立刻向调用方返回 response。没有被 pending request 填充的线程池,没有 timeout,而且极有可能烦人的调用链中断者会更少。此外,下游服务也有了充足的时间来恢复服务能力。完全杜绝错误很难,但是减小失败的影响范围是有可能的。

图解 Go 微服务中的熔断器和重试

 

03

通过 hystrix 熔断器,我们可以采用降级方案,对上游返回降级后的结果。例如,服务 B 可以访问一个备份服务或 cache,不再访问原来的服务 C。引入这种降级方案需要集成测试,因为我们在 hAppy path(译注:所谓 happy path,即测试方法的默认场景,没有异常和错误信息。具体可参见 wikipedia)可能不会遇到这种网络模式。

状态

图解 Go 微服务中的熔断器和重试

 

04

熔断器有三个主要的状态:

  • Closed:让所有请求都通过的默认状态。在阈值下的请求不管成功还是失败,熔断器的状态都不会改变。可能出现的错误是 Max Concurrency(最大并发数)和 Timeout(超时)。
  • Open:所有的请求都会返回 Circuit Open 错误并被标记为失败。这是一种不等待处理结束的 timeout 时间的 fail-fast 机制。
  • Half Open:周期性地向下游服务发出请求,检查它是否已恢复。如果下游服务已恢复,熔断器切换到 Closed 状态,否则熔断器保持 Open 状态。

熔断器原理

控制熔断的设置共有 5 个主要参数。

// CommandConfig is used to tune circuit settings at runtime
type CommandConfig struct {
 Timeout                int `json:"timeout"`
 MaxConcurrentRequests  int `json:"max_concurrent_requests"`
 RequestVolumeThreshold int `json:"request_volume_threshold"`
 SleepWindow            int `json:"sleep_window"`
 ErrorPercentThreshold  int `json:"error_percent_threshold"`
}

查看源码

可以通过根据两个服务的 SLA(‎ Service Level Agreement,服务级别协议)来定出阈值。如果在测试时把依赖的其他服务也涉及到了,这些值会得到很好的调整。

一个好的熔断器的名字应该能精确指出哪个服务连接出了问题。实际上,请求一个服务时可能会有很多个 API endpoint。每一个 endpoint 都应该有一个对应的熔断器。

生产上的熔断器

熔断器通常被放在聚合点上。尽管熔断器提供了一种 fail-fast 机制,但我们仍然需要确保可选的降级方案可行。如果我们因为假定需要降级方案的场景出现的可能性很小就不去测试它,那(之前的努力)就是白费力气了。即使在最简单的演练中,我们也要确保阈值是有意义的。以我的个人经验,把参数配置在 log 中 print 出来对于 debug 很有帮助。

Demo

这段实例代码用的是 hystrix-go 库,hystrix Netflix 库在 Golang 的实现。

package main
import (
 "errors"
 "fmt"
 "log"
 "net/http"
 "os"
 "github.com/afex/hystrix-go/hystrix"
)
const commandName = "producer_api"
func main() {
 hystrix.ConfigureCommand(commandName, hystrix.CommandConfig{
  Timeout:                500,
  MaxConcurrentRequests:  100,
  ErrorPercentThreshold:  50,
  RequestVolumeThreshold: 3,
  SleepWindow:            1000,
 })
 http.HandleFunc("/", logger(handle))
 log.Println("listening on :8080")
 http.ListenAndServe(":8080", nil)
}
func handle(w http.ResponseWriter, r *http.Request) {
 output := make(chan bool, 1)
 errors := hystrix.Go(commandName, func() error {
  // talk to other services
  err := callChargeProducerAPI()
  // err := callWithRetryV1()
  if err == nil {
   output <- true
  }
  return err
 }, nil)
 select {
 case out := <-output:
  // success
  log.Printf("success %v", out)
 case err := <-errors:
  // failure
  log.Printf("failed %s", err)
 }
}
// logger is Handler wrapper function for logging
func logger(fn http.HandlerFunc) http.HandlerFunc {
 return func(w http.ResponseWriter, r *http.Request) {
  log.Println(r.URL.Path, r.Method)
  fn(w, r)
 }
}
func callChargeProducerAPI() error {
 fmt.Println(os.Getenv("SERVER_ERROR"))
 if os.Getenv("SERVER_ERROR") == "1" {
  return errors.New("503 error")
 }
 return nil
}

demo 中分别测试了请求调用链 closed 和 open 两种情况:

/* Experiment 1: success path */
// server
go run main.go
// client
for i in $(seq 10); do curl -x '' localhost:8080 ;done
/* Experiment 2: circuit open */
// server
SERVER_ERROR=1 Go run main.go
// client
for i in $(seq 10); do curl -x '' localhost:8080 ;done

查看源码

重试问题

在上面的熔断器模式中,如果服务 B 缩容,会发生什么?大量已经从 A 发出的请求会返回 5xx error。可能会触发熔断器切换到 open 的错误报警。因此我们需要重试以防间歇性的 network hiccup 发生。

一段简单的重试代码示例:

package main
func callWithRetryV1() (err error) {
 for index := 0; index < 3; index++ {
  // call producer API
  err := callChargeProducerAPI()
  if err != nil {
   return err
  }
 }
 // adding backoff
 // adding jitter
 return nil
}

查看源码

重试模式

为了实现乐观锁,我们可以为不同的服务配置不同的重试次数。因为立即重试会对下游服务产生爆发性的请求,所以不能用立即重试。加一个 backoff 时间可以缓解下游服务的压力。一些其他的模式会用一个随机的 backoff 时间(或在等待时加 jitter)。

一起来看下列算法:

  • Exponential: bash * 2attemp
  • Full Jitter: sleep = rand(0, base * 2attempt)
  • Equal Jitter: temp = base * 2attemp; sleep = temp/2+rand(0, temp/2)
  • De-corredlated Jitter: sleep = rand(base, sleep*3)

【译注】关于这几个算法,可以参考这篇文章 。Full Jitter、 Equal Jitter、 De-corredlated 等都是原作者自己定义的名词。

图解 Go 微服务中的熔断器和重试

 

05

客户端的数量与服务端的总负载和处理完成时间是有关联的。为了确定什么样的重试模式最适合你的系统,在客户端数量增加时很有必要运行基准测试。详细的实验过程可以在这篇文章中看到。我建议的算法是 de-corredlated Jitter 和 full jitter 选择其中一个。

两者结合

图解 Go 微服务中的熔断器和重试

 

Example configuration of both tools

熔断器被广泛用在无状态线上事务系统中,尤其是在聚合点上。重试应该用于调度作业或不被 timeout 约束的 worker。经过深思熟虑后我们可以同时用熔断器和重试。在大型系统中,service mesh 是一种能更精确地编排不同配置的理想架构。

参考文章

  1. https://github.com/afex/hystrix-go/
  2. https://github.com/eApache/go-resiliency
  3. https://github.com/Netflix/Hystrix/wiki
  4. https://www.awsarchitectureblog.com/2015/03/backoff.html
  5. https://dzone.com/articles/go-microservices-part-11-hystrix-and-resilience

via: https://medium.com/@trongdan_tran/circuit-breaker-and-retry-64830e71d0f6

作者:Dan Tran译者:lxbwolf校对:polaris1119

本文由 GCTT 原创编译,Go 中文网 荣誉推出

喜欢本文的朋友,欢迎关注“Go语言中文网”



Tags:Go   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
一. 配置yum源在目录 /etc/yum.repos.d/ 下新建文件 google-chrome.repovim /etc/yum.repos.d/google-chrome.repo按i进入编辑模式写入如下内容:[google-chrome]name=googl...【详细内容】
2021-12-23  Tags: Go  点击:(7)  评论:(0)  加入收藏
昨日谷歌宣布,自2022年12月19日开始停止对OnHub的软件支持,OnHub路由器仍将提供Wi-Fi信号,但用户无法用谷歌Home应用程序管理它。无法更新Wi-Fi网络设置、添加额外的Wifi设备或...【详细内容】
2021-12-22  Tags: Go  点击:(5)  评论:(0)  加入收藏
zip 是一种常见的归档格式,本文讲解 Go 如何操作 zip。首先看看 zip 文件是如何工作的。以一个小文件为例:(类 Unix 系统下)$ cat hello.textHello!执行 zip 命令进行归档:$ zip...【详细内容】
2021-12-17  Tags: Go  点击:(13)  评论:(0)  加入收藏
流水线(Pipeline)是把一个重复的过程分解为若干个子过程,使每个子过程与其他子过程并行进行的技术。本文主要介绍了诞生于云原生时代的流水线框架 Argo。 什么是流水线?在计算机...【详细内容】
2021-11-30  Tags: Go  点击:(21)  评论:(0)  加入收藏
大家好,我是 polarisxu。前段时间,Russ Cox 明确了泛型相关的事情,原计划在标准库中加入泛型相关的包,改放到 golang.org/x/exp 下。目前,Go 泛型的主要设计者 ianlancetaylor 完...【详细内容】
2021-11-30  Tags: Go  点击:(24)  评论:(0)  加入收藏
前言最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。 但现阶段相对来说还是 Python 写的多一些,偶尔还...【详细内容】
2021-11-25  Tags: Go  点击:(29)  评论:(0)  加入收藏
前几节课我们学习了Django加载网页数据的相关知识,今天我们讲一下怎么加载静态文件,我们以加载图片为例,学习怎么配置静态文件。 1.思路讲解 首先我们需要新建文件(test2)作为我...【详细内容】
2021-11-23  Tags: Go  点击:(43)  评论:(0)  加入收藏
在本教程中,我们将介绍如何使用 Django 发送电子邮件。我们将介绍如何配置 Django SMTP 连接,如何为您的电子邮件提供商设置应用程序密码,以及如何通过 Django shell 发送电子...【详细内容】
2021-11-10  Tags: Go  点击:(22)  评论:(0)  加入收藏
golang context 很好用,就使用php实现了github地址 : https://github.com/qq1060656096/php-go-context context使用闭坑指南1. 将一个Context参数作为第一个参数传递给传入和...【详细内容】
2021-11-05  Tags: Go  点击:(41)  评论:(0)  加入收藏
谷歌宣布调整服务费费率,从明年起Google Play上所有付费订阅的抽成将从30%降低到15%。此外,电子书和点播音乐流媒体服务还将有资格享受低至10%的费率。此前,Google Play上的开...【详细内容】
2021-10-28  Tags: Go  点击:(38)  评论:(0)  加入收藏
▌简易百科推荐
zip 是一种常见的归档格式,本文讲解 Go 如何操作 zip。首先看看 zip 文件是如何工作的。以一个小文件为例:(类 Unix 系统下)$ cat hello.textHello!执行 zip 命令进行归档:$ zip...【详细内容】
2021-12-17  Go语言中文网    Tags:Go语言   点击:(13)  评论:(0)  加入收藏
大家好,我是 polarisxu。前段时间,Russ Cox 明确了泛型相关的事情,原计划在标准库中加入泛型相关的包,改放到 golang.org/x/exp 下。目前,Go 泛型的主要设计者 ianlancetaylor 完...【详细内容】
2021-11-30  Go语言中文网    Tags:slices 包   点击:(24)  评论:(0)  加入收藏
前言最近因为项目需要写了一段时间的 Go ,相对于 Java 来说语法简单同时又有着一些 Python 之类的语法糖,让人大呼”真香“。 但现阶段相对来说还是 Python 写的多一些,偶尔还...【详细内容】
2021-11-25  crossoverJie    Tags:Go   点击:(29)  评论:(0)  加入收藏
go-micro是基于 Go 语言用于开发的微服务的 RPC 框架,主要功能如下:服务发现,负载均衡 ,消息编码,请求/响应,Async Messaging,可插拔接口,最后这个功能牛p安装步骤安装proto...【详细内容】
2021-09-06    石老师小跟班  Tags:go-micro   点击:(197)  评论:(0)  加入收藏
GoLand 2021.2 EAP 5 现已发布。用户可以从工具箱应用程序中获得 EAP 构建,也可以从官方网站手动下载。并且从此 EAP 开始,只有拥有有效的 JetBrains 帐户才能加入该计划。手...【详细内容】
2021-06-29  IT实战联盟  今日头条  Tags:GoLand   点击:(185)  评论:(0)  加入收藏
作者:HDT3213今天给大家带来的开源项目是 Godis:一个用 Go 语言实现的 Redis 服务器。支持: 5 种数据结构(string、list、hash、set、sortedset) 自动过期(TTL) 发布订阅、地理位...【详细内容】
2021-06-18  HelloGitHub  今日头条  Tags:Go   点击:(125)  评论:(0)  加入收藏
统一规范篇合理规划目录本篇主要描述了公司内部同事都必须遵守的一些开发规矩,如统一开发空间,既使用统一的开发工具来保证代码最后的格式的统一,开发中对文件和代码长度的控制...【详细内容】
2021-05-18  1024课堂    Tags:Go语言   点击:(232)  评论:(0)  加入收藏
闭包概述 闭包不是Go语言独有的概念,在很多编程语言中都有闭包 闭包就是解决局部变量不能被外部访问的一种解决方案 是把函数当作返回值的一种应用 代码演示总体思想:在函数...【详细内容】
2021-05-14  HelloGo  今日头条  Tags:Go语言   点击:(223)  评论:(0)  加入收藏
一时想不开,想了解一下Go语言,于是安装了并体验了一下。下载1. 进入golang.google.cn 点击Download Go 2.选择对应的操作系统,点击后开始下载。 安装1. windows下执行傻瓜式安...【详细内容】
2021-05-12  程序员fearlazy  fearlazy  Tags:Go语言   点击:(236)  评论:(0)  加入收藏
1.简介channel是Go语言的一大特性,基于channel有很多值得探讨的问题,如 channel为什么是并发安全的? 同步通道和异步通道有啥区别? 通道为何会阻塞协程? 使用通道导致阻塞的协程...【详细内容】
2021-05-10  程序员麻辣烫  今日头条  Tags:Go通道   点击:(274)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条