go-kit 微服务 服务熔断(hystrix-go 实现)
Hystrix
- 在微服务架构中,每个服务都是相互关联的,比如我们下单服务和扣钱服务是分开的,现在扣钱服务出现的bug不能正常服务
- Hystrix可以让我们在在微服务架构中对服务间的调用进行控制,加入一些调用延迟或者服务降级的容错机制。
Hystrix的设计原则
- 对依赖服务调用时出现的调用延迟和调用失败进行控制和容错保护
- 在复杂的分布式系统中,阻止某一个依赖服务的故障在整个系统中蔓延
- 提供fail-fast(快速失败)和快速恢复的支持
- 提供fallback优雅降级的支持
- 支持近实时的监控、报警以及运维操作
编写Hystrix类
import (
"errors"
"github.com/afex/hystrix-go/hystrix"
"sync"
)
var config = hystrix.CommandConfig{
Timeout: 5000, //执行command的超时时间(毫秒)
MaxConcurrentRequests: 8, //command的最大并发量
SleepWindow: 1000, //过多长时间,熔断器再次检测是否开启。单位毫秒
ErrorPercentThreshold: 30, //错误率 请求数量大于等于RequestVolumeThreshold并且错误率到达这个百分比后就会启动
RequestVolumeThreshold: 5, //请求阈值(一个统计窗口10秒内请求数量) 熔断器是否打开首先要满足这个条件;这里的设置表示至少有5个请求才进行ErrorPercentThreshold错误百分比计算
}
type runFunc func() error
type Hystrix struct {
loadMap *sync.Map //储存每个调用函数对应的 Hystrix
fallback string //降级信息
}
func NewHystrix(msg string) *Hystrix {
return &Hystrix{
loadMap: new(sync.Map),
fallback: msg,
}
}
func (s *Hystrix) Run(name string, run runFunc) error {
if _, ok := s.loadMap.Load(name); !ok {
hystrix.ConfigureCommand(name, config)
s.loadMap.Store(name, name)
}
//name 为执行的命令名称
//run 我们要执行的函数
//fallback:run运行过程中发生错误时的回调方法
err := hystrix.Do(name, func() error {
return run()
}, func(err error) error {
//fmt.Println("运行 run 方法错误", err)
return errors.New(s.fallback)
})
if err != nil {
return err
}
return nil
}
添加Hystrix到调用客户端
- 这里为了展示Hystrix的状态去掉了一些日志信息
hy := utils.NewHystrix("调用错误服务降级")
cbs, _, _ := hystrix.GetCircuit("login")
for i := 0; i < 100; i++ {
time.Sleep(time.Millisecond * 100)
userAgent, err := client.UserAgentClient()
if err != nil {
t.Error(err)
return
}
err = hy.Run("login", func() error {
_, err := userAgent.Login(context.Background(), &pb.Login{
Account: "hwholiday",
Password: "123456",
})
if err != nil {
return err
}
//fmt.Println(ack.Token)
return nil
})
fmt.Println("熔断器开启状态:", cbs.IsOpen(), "请求是否允许:", cbs.AllowRequest())
if err != nil {
t.Log(err)
}
}
去掉服务端的请求限制功能
func NewEndPointServer(svc Service, limit *rate.Limiter) EndPointServer {
var loginEndPoint endpoint.Endpoint
{
loginEndPoint = MakeLoginEndPoint(svc)
//loginEndPoint = NewGolangRateAllowMiddleware(limit)(loginEndPoint)
}
return EndPointServer{LoginEndPoint: loginEndPoint}
}
模拟服务器错误功能
if in.Account != "hwholiday" || in.Password != "123456" {
err = errors.New("用户信息错误")
return
}
//模拟耗时
//rand.Seed(time.Now().UnixNano())
//sl := rand.Int31n(10-1) + 1
//time.Sleep(time.Duration(sl) * time.Millisecond * 100)
//模拟错误
if rand.Intn(10) > 3 {
err = errors.New("服务器运行错误")
return
}
ack = &pb.LoginAck{}
运行
- 运行 TestNewUserAgentClient 方法 (调用Login接口100次)
熔断器开启状态: true 请求是否允许: false
熔断器开启状态: true 请求是否允许: false
s.LoginEndPoint rpc error: code = Unknown desc = 服务器运行错误
s.LoginEndPoint rpc error: code = Unknown desc = 服务器运行错误
s.LoginEndPoint rpc error: code = Unknown desc = 服务器运行错误
s.LoginEndPoint rpc error: code = Unknown desc = 服务器运行错误 (previously: rpc error: code = Unknown desc = 服务器运行错误; rpc error: code = Unknown desc = 服务器运行错误)
熔断器开启状态: true 请求是否允许: false
熔断器开启状态: true 请求是否允许: false
熔断器开启状态: true 请求是否允许: false
熔断器开启状态: true 请求是否允许: false
熔断器开启状态: true 请求是否允许: false
熔断器开启状态: true 请求是否允许: false
熔断器开启状态: true 请求是否允许: false
熔断器开启状态: true 请求是否允许: false
熔断器开启状态: true 请求是否允许: false
熔断器开启状态: true 请求是否允许: false
熔断器开启状态: false 请求是否允许: true
s.LoginEndPoint rpc error: code = Unknown desc = 服务器运行错误
熔断器开启状态: false 请求是否允许: true
结语
- 我们可以看到Hystrix的状态从 打开 ->关闭 -> 打开
- Hystrix的用法还有很多,这里只展示简单的使用,更加高级的功能欢迎大家一起讨论
- 欢迎添加QQ一起讨论
联系 QQ: 3355168235