Golang微服务:Micro限流、熔断
Wrapper
Wrapper提供了一种包装机制,使得在执行某方法前先执行Wrapper,优点Filter的意思;因此可以在客户端和服务器做很多功能:熔断限流、Filter、Auth等。
client代码如下:调用greeter.Hello时先执行logWrap.Call方法,再调用RPC请求。
// log wrapper logs every time a request is made
type logWrapper struct {
client.Client
}
func (l *logWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
fmt.Printf("[wrapper] client request service: %s method: %s\n", req.Service(), req.Method())
return l.Client.Call(ctx, req, rsp)
}
// Implements client.Wrapper as logWrapper
func logWrap(c client.Client) client.Client {
return &logWrapper{c}
}
func main() {
service := micro.NewService(
micro.Name("greeter.client"),
micro.Registry(mdns.NewRegistry()),
// wrap the client
micro.WrapClient(logWrap),
)
service.Init()
greeter := proto.NewGreeterService("greeter", service.Client())
rsp, err := greeter.Hello(context.TODO(), &proto.HelloRequest{Name: "John"})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(rsp.Greeting)
}
server代码如下:当RPC调用进来时先执行logWrapper,再执行Hello
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error {
rsp.Greeting = "Hello " + req.Name
log.Println("in Hello")
return nil
}
// logWrapper is a handler wrapper
func logWrapper(fn server.HandlerFunc) server.HandlerFunc {
return func(ctx context.Context, req server.Request, rsp interface{}) error {
log.Printf("[wrapper] server request: %v", req.Method())
err := fn(ctx, req, rsp)
return err
}
}
func main() {
service := micro.NewService(
micro.Name("greeter"),
// wrap the handler
micro.WrapHandler(logWrapper),
micro.Registry(mdns.NewRegistry()),
)
service.Init()
proto.RegisterGreeterHandler(service.Server(), new(Greeter))
if err := service.Run(); err != nil {
fmt.Println(err)
}
}
熔断
Micro提供了两种实现,gobreaker和hystrix,熔断是在客户端实现。先看看 hystrix:
var (
// DefaultTimeout is how long to wait for command to complete, in milliseconds
DefaultTimeout = 1000
// DefaultMaxConcurrent is how many commands of the same type can run at the same time
DefaultMaxConcurrent = 10
// DefaultVolumeThreshold is the minimum number of requests needed before a circuit can be tripped due to health
DefaultVolumeThreshold = 20
// DefaultSleepWindow is how long, in milliseconds, to wait after a circuit opens before testing for recovery
DefaultSleepWindow = 5000
// DefaultErrorPercentThreshold causes circuits to open once the rolling measure of errors exceeds this percent of requests
DefaultErrorPercentThreshold = 50
// DefaultLogger is the default logger that will be used in the Hystrix package. By default prints nothing.
DefaultLogger = NoopLogger{}
)
type Settings struct {
Timeout time.Duration
MaxConcurrentRequests int
RequestVolumeThreshold uint64
SleepWindow time.Duration
ErrorPercentThreshold int
}
hystrix会根据这5个参数(超时时间、并发请求数、请求量、空歇床、错误率)来选择合适的服务进行调度,目前是使用的 hystrix提供的默认参数,不支持自定义参数,示例:
func TestBreaker(t *testing.T) {
// setup
r := mock.NewRegistry()
s := selector.NewSelector(selector.Registry(r))
c := client.NewClient(
// set the selector
client.Selector(s),
// add the breaker wrapper
client.Wrap(NewClientWrapper()),
)
req := c.NewRequest("test.service", "Test.Method", map[string]string{
"foo": "bar",
}, client.WithContentType("application/json"))
var rsp map[string]interface{}
// Force to point of trip
for i := 0; i < (hystrix.DefaultVolumeThreshold * 3); i++ {
c.Call(context.TODO(), req, rsp)
}
err := c.Call(context.TODO(), req, rsp)
if err == nil {
t.Error("Expecting tripped breaker, got nil error")
}
if err.Error() != "hystrix: circuit open" {
t.Errorf("Expecting tripped breaker, got %v", err)
}
}
gobreaker方案与hystrix类似,可以自定义参数。
限流
ratelimit可以在客户端做,也可以在服务端做;micro提供了两种方案:juju/ratelimit
、uber/ratelimit
。
客户端实现:
func TestRateClientLimit(t *testing.T) {
// setup
r := mock.NewRegistry()
s := selector.NewSelector(selector.Registry(r))
testRates := []int{1, 10, 20, 100}
for _, limit := range testRates {
b := ratelimit.NewBucketWithRate(float64(limit), int64(limit))
c := client.NewClient(
// set the selector
client.Selector(s),
// add the breaker wrapper
client.Wrap(NewClientWrapper(b, false)),//fasle=快速失败?
)
req := c.NewRequest(
"test.service",
"Test.Method",
&TestRequest{},
client.WithContentType("application/json"),
)
rsp := TestResponse{}
for j := 0; j < limit; j++ {
err := c.Call(context.TODO(), req, &rsp)
e := errors.Parse(err.Error())
if e.Code == 429 {
t.Errorf("Unexpected rate limit error: %v", err)
}
}
err := c.Call(context.TODO(), req, rsp)
e := errors.Parse(err.Error())
if e.Code != 429 {
t.Errorf("Expected rate limit error, got: %v", err)
}
}
}
- NewBucketWithRate入参为速率(QPS)和容量(CAP),比如每秒5个请求,最大保持50个活动的请求
- NewClientWrapper第二个参数wait,指示当受到限流时是否等待,如果是false即快速失败,返回(429,too mant request)
服务端实现(以下代码包含了客户端测试代码):
func TestRateServerLimit(t *testing.T) {
// setup
r := mock.NewRegistry()
s := selector.NewSelector(selector.Registry(r))
testRates := []int{1, 10, 20}
for _, limit := range testRates {
b := ratelimit.NewBucketWithRate(float64(limit), int64(limit))
c := client.NewClient(client.Selector(s))
name := fmt.Sprintf("test.service.%d", limit)
s := server.NewServer(
server.Name(name),
// add registry
server.Registry(r),
// add the breaker wrapper
server.WrapHandler(NewHandlerWrapper(b, false)),
)
type Test struct {
*testHandler
}
s.Handle(
s.NewHandler(&Test{new(testHandler)}),
)
if err := s.Start(); err != nil {
t.Fatalf("Unexpected error starting server: %v", err)
}
if err := s.Register(); err != nil {
t.Fatalf("Unexpected error registering server: %v", err)
}
req := c.NewRequest(name, "Test.Method", &TestRequest{}, client.WithContentType("application/json"))
rsp := TestResponse{}
for j := 0; j < limit; j++ {
if err := c.Call(context.TODO(), req, &rsp); err != nil {
t.Fatalf("Unexpected request error: %v", err)
}
}
err := c.Call(context.TODO(), req, &rsp)
if err == nil {
t.Fatalf("Expected rate limit error, got nil: rate %d, err %v", limit, err)
}
e := errors.Parse(err.Error())
if e.Code != 429 {
t.Fatalf("Expected rate limit error, got %v", err)
}
s.Deregister()
s.Stop()
// artificial test delay
time.Sleep(time.Millisecond * 20)
}
}
client.NewClient支持多个Wrapper,将熔断限流功能都添加上
import "github.com/micro/go-plugins/wrapper/breaker/hystrix"
import "github.com/micro/go-plugins/wrapper/ratelimiter/ratelimit"
c := client.NewClient(
client.Wrap(ratelimit.NewClientWrapper(b, false)),
client.Wrap(hystrixNewClientWrapper()),
)
Golang微服务:Micro限流、熔断的更多相关文章
- 微服务容错限流Hystrix入门
为什么需要容错限流 复杂分布式系统通常有很多依赖,如果一个应用不能对来自依赖 故障进行隔离,那么应用本身就处在被拖垮的风险中.在一个高流量的网站中,某个单一后端一旦发生延迟,将会在数秒内导致 所有应用 ...
- springcloud3(六) 服务降级限流熔断组件Resilience4j
代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-gateway/src/test/java/com/ ...
- 微服务组件--限流框架Spring Cloud Hystrix分析
Hystrix的介绍 [1]Hystrix是springCloud的组件之一,Hystrix 可以让我们在分布式系统中对服务间的调用进行控制加入一些调用延迟或者依赖故障的容错机制. [2]Hystri ...
- Hystrix介绍以及服务的降级限流熔断
(dubbo熔断,Hystrix问的少) 无论是缓存层还是存储层都会有出错的概率,可以将它们视同为资源.作为并发量较大的系统,假如有一个资源不可用,可能会造成线程全部 hang (挂起)在这个资源上, ...
- .Net微服务实践(四)[网关]:Ocelot限流熔断、缓存以及负载均衡
目录 限流 熔断 缓存 Header转化 HTTP方法转换 负载均衡 注入/重写中间件 后台管理 最后 在上篇.Net微服务实践(三)[网关]:Ocelot配置路由和请求聚合中我们介绍了Ocelot的 ...
- golang微服务框架go-micro 入门笔记2.3 micro工具之消息接收和发布
本章节阐述micro消息订阅和发布相关内容 阅读本文前你可能需要进行如下知识储备 golang分布式微服务框架go-micro 入门笔记1:搭建go-micro环境, golang微服务框架go-mi ...
- golang微服务框架go-micro 入门笔记2.2 micro工具之微应用利器micro web
micro web micro 功能非常强大,本文将详细阐述micro web 命令行的功能 阅读本文前你可能需要进行如下知识储备 golang分布式微服务框架go-micro 入门笔记1:搭建go- ...
- golang微服务框架go-micro 入门笔记2.1 micro工具之micro api
micro api micro 功能非常强大,本文将详细阐述micro api 命令行的功能 重要的事情说3次 本文全部代码https://idea.techidea8.com/open/idea.s ...
- .Net Core使用Ocelot网关(一) -负载,限流,熔断,Header转换
1.什么是API网关 API网关是微服务架构中的唯一入口,它提供一个单独且统一的API入口用于访问内部一个或多个API.它可以具有身份验证,监控,负载均衡,缓存,请求分片与管理,静态响应处理等.API ...
- .Net Core的API网关Ocelot的使用(二)[负载,限流,熔断,Header转换]
网关的负载均衡 当下游拥有多个节点的时候,我们可以用DownstreamHostAndPorts来配置 { "UpstreamPathTemplate": "/Api_A ...
随机推荐
- 回文的范围——算法面试刷题2(for google),考察前缀和
如果一个正整数的十进制表示(没有前导零)是一个回文字符串(一个前后读取相同的字符串),那么它就是回文.例如,数字5, 77, 363, 4884, 11111, 12121和349943都是回文. 如 ...
- ldap 导出、导入ldif数据
ldap 导出.导入ldif数据有如下方式: 1.dsadm(速度快,需要停止ldap实例) 2.dsconf(速度慢,需要保持ldap实例开启) windows导出.导入需要加上参数--unsecu ...
- Angular2 之父子组件交互方式
父子组件交互方式,这里介绍主要的三种方式 1.事件传值 下面以列表页和分页组件举例. list.component.html <pagination *ngIf="pageParams ...
- 修改 input中的placeholder的字体样式和颜色
placeholder属性是css3中新增加的属性, 由于是新加入的属性因此对各大浏览器都不兼容: 因此在使用的时候要加兼容性 火狐:-moz-placeholder { /* Mozilla Fir ...
- SQL-54 查找排除当前最大、最小salary之后的员工的平均工资avg_salary。
题目描述 查找排除当前最大.最小salary之后的员工的平均工资avg_salary.CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL,`sala ...
- 随手心得(浅谈iOS)
前一段时间去一个公司面试,面试官问我关于iOS的ARC,当然ARC对于一般有经验的iOS程序员来说一般不是什么问题,但是他问我苹果是怎么实现的,我就说通过地址指针解决的,然后他问我那苹果指针指向地址是 ...
- 第一个HTML文档
属性 和 值 1.作用 用来修饰元素 ex:让 p 标记的文本水平居中对齐 <p>Hello World</p> 2.语法 1.属性的声明必须位于开始标记里 ...
- type-of-python作业-判断字符串是否属于回文需要忽略其中的标点、空格与大小写
type-of-python作业 作业练习:要想检查文本是否属于回文需要忽略其中的标点.空格与大小写.例如,"Rise to vote, sir."是一段回文文本,但是我们现有的程 ...
- 解决Visual C++ for Linux: -L"~/projects/path_to_lib_folder" 无法设置library search path的问题
最近倒腾Linux C/C++项目.以目前的情况来说,要生成编译(build)一个Linux工程脚本,首选的工具必定是CMake.这也是我之前Linux项目的首选.不过自从VS IDE支持Linux ...
- MySQL从本地向数据库导入数据
本文来自:https://www.cnblogs.com/lettuce-u/p/10715795.html(自己收藏看) 在localhost中准备好了一个test数据库和一个pet表: mysql ...