ホームページ  >  記事  >  バックエンド開発  >  hystrix-goの使い方と原理を詳しく解説

hystrix-goの使い方と原理を詳しく解説

藏色散人
藏色散人転載
2020-12-29 09:25:404217ブラウズ

次のコラム golang チュートリアルでは、bash コマンド解析を実行するための bash 関数をカプセル化する golang について紹介します。

hystrix-goの使い方と原理を詳しく解説

冒頭

今週、内部回路ブレーカーの電流制限パッケージを調べていたところ、それがオープンソース プロジェクトに基づいていることがわかりましたhystrix-go それが実現したので、この記事を作成します。

Hystrix

Hystrix は、Netflex によって開発されたオープン ソース コンポーネントであり、基本的な融合機能を提供します。 Hystrix は、ダウングレード戦略を Command にカプセル化し、runfallback という 2 つのメソッドを提供します。前者は、間の呼び出しなどの通常のロジックを表します。 microservices... 障害が発生した場合、fallback メソッドが実行されて結果が返されるため、動作が保証されていると理解できます。通常のロジックが短期間に頻繁に失敗すると、短絡がトリガーされる可能性があります。つまり、後続のリクエストは run を実行せず、fallback を直接実行します。 Hystrix の詳細については、https://github.com/NETFLIX/Hystrix を参照してください。
hystrix-go が使用されています go 実装の hystrix バージョン、より正確には、簡易バージョン。それは単なる最後の更新ですか、それとも 2018 年の pr、つまり卒業を意味しますか?

なぜこれらのツールが必要なのでしょうか?
たとえば、マイクロサービス ベースの製品ラインでは、以下に示すように、各サービスは独自のビジネスに焦点を当て、対応するサービス インターフェイスを外部に提供するか、外部サービスの特定の論理インターフェイスに依存します。

現在 サービス A であるとします。一部のロジックは サービス C に依存し、 サービス C に依存します。サービス E、現在のマイクロサービスは rpc または http と通信しています。タイムアウトなどの理由で、この時点で サービス C がサービス E の呼び出しに失敗すると想定されます。またはネットワークの変動により、サービス E の過負荷によりシステム E がダウンしました。

呼び出しが失敗した場合、通常は失敗時の再試行などのメカニズムがあります。しかし、もう一度考えてみてください。サービス E がすでに利用できなくなっていると仮定すると、この時点でも新しいコールが生成され続け、コール待機や再試行の失敗が伴い、サービス C からサービス E へのコールの大量のバックログが発生することになります。サービス C のリソースが枯渇すると、サービス C もダウンしてしまい、この悪循環はマイクロサービス システム全体に影響を及ぼし、雪崩現象を引き起こします。

雪崩の原因はこれだけではありませんが、この悪夢が起こらないようにするためには、何らかの対策を講じる必要があります。 hystrix-go は、非常に優れたサーキット ブレーカーとダウングレード対策を提供します。その主なアイデアは、同時実行の最大数 (同時実行の数が設定された同時実行の数よりも大きい場合、インターセプト)、エラー率のパーセンテージ (リクエストの数が設定された同時実行数以上の場合) など、いくつかのしきい値を設定することです。設定されたしきい値、エラー率が設定されたパーセンテージに達する、ヒューズのトリガー)、およびヒューズの試行回復時間など。

使用方法

hystrix-go は非常に簡単に使用でき、Go または Do と呼び出すことができます。 メソッド、Go メソッドだけが非同期です。 Do メソッドは同期です。簡単な例から始めましょう。

_ = hystrix.Do("wuqq", func() error {
        // talk to other services
        _, err := http.Get("https://www.baidu.com/")
        if err != nil {
            fmt.Println("get error:%v",err)
            return err        }
        return nil
    }, func(err error) error {
        fmt.Printf("handle  error:%v\n", err)
        return nil
    })

Do この関数には 3 つのパラメータが必要です。最初のパラメータは commmand 名前です。各名前を独立したサービスとして扱うことができます。2 番目のパラメータはプロセスの通常のロジックですサービスを呼び出す http など、戻りパラメータは err です。 | 処理呼び出しが失敗した場合、保証された操作と呼ばれる 3 番目のパラメーター ロジックが実行されます。サービス エラー率が高すぎてヒューズが開いているため、後続のリクエストもこの関数を直接コールバックします。

ヒューズは設定されたルールに従ってオンまたはオフになるため、もちろん必要な値を設定できます。

hystrix.ConfigureCommand("wuqq", hystrix.CommandConfig{
        Timeout:                int(3 * time.Second),
        MaxConcurrentRequests:  10,
        SleepWindow:            5000,
        RequestVolumeThreshold: 10,
        ErrorPercentThreshold:  30,
    })
    _ = hystrix.Do("wuqq", func() error {
        // talk to other services
        _, err := http.Get("https://www.baidu.com/")
        if err != nil {
            fmt.Println("get error:%v",err)
            return err        }
        return nil
    }, func(err error) error {
        fmt.Printf("handle  error:%v\n", err)
        return nil
    })

上記で設定した値の意味を簡単に説明しましょう:

  • Timeout: 执行 command 的超时时间。
  • MaxConcurrentRequests:command 的最大并发量 。
  • SleepWindow:当熔断器被打开后,SleepWindow 的时间就是控制过多久后去尝试服务是否可用了。
  • RequestVolumeThreshold: 一个统计窗口10秒内请求数量。达到这个请求数量后才去判断是否要开启熔断
  • ErrorPercentThreshold:错误百分比,请求数量大于等于RequestVolumeThreshold并且错误率到达这个百分比后就会启动熔断

当然你不设置的话,那么自动走的默认值。

我们再来看一个简单的例子:

package mainimport (
   "fmt"
 "github.com/afex/hystrix-go/hystrix" "net/http" "time")type Handle struct{}func (h *Handle) ServeHTTP(r http.ResponseWriter, request *http.Request) {
   h.Common(r, request)}func (h *Handle) Common(r http.ResponseWriter, request *http.Request) {
   hystrix.ConfigureCommand("mycommand", hystrix.CommandConfig{
      Timeout:                int(3 * time.Second),
      MaxConcurrentRequests:  10,
      SleepWindow:            5000,
      RequestVolumeThreshold: 20,
      ErrorPercentThreshold:  30,
   })
   msg := "success"

  _ = hystrix.Do("mycommand", func() error {
      _, err := http.Get("https://www.baidu.com")
      if err != nil {
         fmt.Printf("请求失败:%v", err)
         return err  }
      return nil
  }, func(err error) error {
      fmt.Printf("handle  error:%v\n", err)
      msg = "error"
  return nil
  })
   r.Write([]byte(msg))}func main() {
   http.ListenAndServe(":8090", &Handle{})}

我们开启了一个 http 服务,监听端口号 8090,所有请求的处理逻辑都在  Common 方法中,在这个方法中,我们主要是发起一次 http请求,请求成功响应success,如果失败,响应失败原因。

我们再写另一个简单程序,并发 11 次的请求 8090 端口。

package mainimport (
    "fmt"
    "io/ioutil"
    "net/http"
    "sync"
    "time")var client *http.Clientfunc init() {
    tr := &http.Transport{
        MaxIdleConns:    100,
        IdleConnTimeout: 1 * time.Second,
    }
    client = &http.Client{Transport: tr}}type info struct {
    Data interface{} `json:"data"`}func main() {
    var wg sync.WaitGroup    for i := 0; i <p>由于我们配置 <code>MaxConcurrentRequests</code> 为10,那么意味着还有个 g 请求会失败:<br><img src="https://img.php.cn/upload/article/000/000/020/a47bae3dcfa1730bb3b1136eddb4f59b-5.png" alt=""><br>和我们想的一样。</p><p>接着我们把网络断开,并发请求改成10次。再次运行程序并发请求 <code>8090</code> 端口,此时由于网络已关闭,导致请求百度失败:<br><img src="https://img.php.cn/upload/article/000/000/020/a47bae3dcfa1730bb3b1136eddb4f59b-6.png" alt=""><br>接着继续请求:<br><img src="https://img.php.cn/upload/article/000/000/020/a47bae3dcfa1730bb3b1136eddb4f59b-7.png" alt=""><br>熔断器已开启,上面我们配置的<code>RequestVolumeThreshold</code> 和 <code>ErrorPercentThreshold</code> 生效。</p><p>然后我们把网连上,五秒后 (<code>SleepWindow</code>的值)继续并发调用,当前熔断器处于半开的状态,此时请求允许调用依赖,如果成功则关闭,失败则继续开启熔断器。<br><img src="https://img.php.cn/upload/article/000/000/020/a47bae3dcfa1730bb3b1136eddb4f59b-8.png" alt=""><br>可以看到,有一个成功了,那么此时熔断器已关闭,接下来继续运行函数并发调用:<br><img src="https://img.php.cn/upload/article/000/000/020/24be5795ca6d697af3d1d7ffdfdafc03-9.png" alt=""><br>可以看到,10个都已经是正常成功的状态了。</p><p>那么问题来了,为什么最上面的图只有一个是成功的?5秒已经过了,并且当前网络正常,应该是10个请求都成功,但是我们看到的只有一个是成功状态。通过源码我们可以找到答案:<br>具体逻辑在判断当前请求是否可以调用依赖</p><pre class="brush:php;toolbar:false">if !cmd.circuit.AllowRequest() {
            ......
            return
        }
func (circuit *CircuitBreaker) AllowRequest() bool {
    return !circuit.IsOpen() || circuit.allowSingleTest()}func (circuit *CircuitBreaker) allowSingleTest() bool {
    circuit.mutex.RLock()
    defer circuit.mutex.RUnlock()

    now := time.Now().UnixNano()
    openedOrLastTestedTime := atomic.LoadInt64(&circuit.openedOrLastTestedTime)
    if circuit.open && now > openedOrLastTestedTime+getSettings(circuit.Name).SleepWindow.Nanoseconds() {
    /
        swapped := atomic.CompareAndSwapInt64(&circuit.openedOrLastTestedTime, openedOrLastTestedTime, now) //这一句才是关键
        if swapped {
            log.Printf("hystrix-go: allowing single test to possibly close circuit %v", circuit.Name)
        }
        return swapped    }

    return false}

这段代码首先判断了熔断器是否开启,并且当前时间大于 上一次开启熔断器的时间+ SleepWindow 的时间,如果条件都符合的话,更新此熔断器最新的 openedOrLastTestedTime ,是通过  CompareAndSwapInt64 原子操作完成的,意外着必然只会有一个成功。
此时熔断器还是半开的状态,接着如果能拿到令牌,执行run 函数(也就是Do传入的第二个简单封装后的函数),发起 http 请求,如果成功,上报成功状态,关闭熔断器。如果失败,那么熔断器依旧开启。

hystrix-goの使い方と原理を詳しく解説

以上就是大体的流程讲解,下一篇文章将解读核心源码以及进一步当思考。

更多相关技术文章,请访问go语言教程栏目!                                                  

以上がhystrix-goの使い方と原理を詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlearnku.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。