Describe the bug
ServiceMiddleware因重新赋值,导致next mw紊乱
To Reproduce
如下,背景是使用kitex multiservice的模式,在注册s2时,我想使用ServiceMiddleware,打印"ServiceMiddleware Run"。预期是s2的接口会进入此mw并打印一次,s1的接口不会进入。 但事实发现只要s2的接口被调用一次后,s1和s2的接口都能进入此mw, 并且随着s2的接口调用次数变多,此mw执行次数线性增长。举个例子s2接口调用10次后,再次调用s2接口,会进入此mw 11次,打印11次
func main() {
defer logs.Stop()
var (
s1 ServiceA = new(_s1)
s2 ServiceB = new(_s2)
)
svc := motorpriceservice.NewServer(s1, server.WithMiddleware(func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req, resp interface{}) (err error) {
fmt.Print("loglog")
err = next(ctx, req, resp)
return
}
}))
if err := tradepricecoreservice.RegisterService(svc, s2, cwgsvr.WithServiceMiddleware(
func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req, resp interface{}) (err error) {
fmt.Print("ServiceMiddleware Run")
return next(ctx, req, resp)
}
},
)); err != nil {
panic(err)
}
if err := svc.Run(); err != nil {
panic(err)
}
}
推测问题在于此处重新赋值next,导致闭包变量产生了变哈。
本来调用栈 next1->next2->next3->next4,如果在执行next3的时候换了闭包变量next, 变成next1->next2->next3->next5->next4->invoke;如果再次调用变成 成next1->next2->next3->next5->next5->next4->invoke, 随着调用次数变多,此链路会线性增长,执行非预期的次数,更会导致运行时调用栈溢出。。更不用说并发情况下的更复杂的问题。
用了单测复现根因
func TestChain(t *testing.T) {
stuckMW := func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req, resp interface{}) (err error) {
t.Log("layer stuck")
return next(ctx, req, resp)
}
}
c := endpoint.Chain(func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req, resp interface{}) (err error) {
t.Log("layer1")
return next(ctx, req, resp)
}
},
func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req, resp interface{}) (err error) {
t.Log("layer2")
next = stuckMW(next)
return next(ctx, req, resp)
}
},
func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, req, resp interface{}) (err error) {
t.Log("layer3")
return next(ctx, req, resp)
}
},
)
invokeChain := c(func(ctx context.Context, req, resp interface{}) (err error) {
t.Log("invoke")
return
})
for i := 0; i < 4; i++ {
_ = invokeChain(context.Background(), nil, nil)
}
}
输出结果
=== RUN TestChain
cmd_test.go:27: layer1
cmd_test.go:33: layer2
cmd_test.go:20: layer stuck
cmd_test.go:40: layer3
cmd_test.go:46: invoke
cmd_test.go:27: layer1
cmd_test.go:33: layer2
cmd_test.go:20: layer stuck
cmd_test.go:20: layer stuck
cmd_test.go:40: layer3
cmd_test.go:46: invoke
cmd_test.go:27: layer1
cmd_test.go:33: layer2
cmd_test.go:20: layer stuck
cmd_test.go:20: layer stuck
cmd_test.go:20: layer stuck
cmd_test.go:40: layer3
cmd_test.go:46: invoke
cmd_test.go:27: layer1
cmd_test.go:33: layer2
cmd_test.go:20: layer stuck
cmd_test.go:20: layer stuck
cmd_test.go:20: layer stuck
cmd_test.go:20: layer stuck
cmd_test.go:40: layer3
cmd_test.go:46: invoke
Environment:
- Kitex version: 0.14.1
- Protocol: thrift
Describe the bug
ServiceMiddleware因重新赋值,导致next mw紊乱
To Reproduce
如下,背景是使用kitex multiservice的模式,在注册s2时,我想使用ServiceMiddleware,打印"ServiceMiddleware Run"。预期是s2的接口会进入此mw并打印一次,s1的接口不会进入。 但事实发现只要s2的接口被调用一次后,s1和s2的接口都能进入此mw, 并且随着s2的接口调用次数变多,此mw执行次数线性增长。举个例子s2接口调用10次后,再次调用s2接口,会进入此mw 11次,打印11次
推测问题在于此处重新赋值next,导致闭包变量产生了变哈。
本来调用栈 next1->next2->next3->next4,如果在执行next3的时候换了闭包变量next, 变成next1->next2->next3->next5->next4->invoke;如果再次调用变成 成next1->next2->next3->next5->next5->next4->invoke, 随着调用次数变多,此链路会线性增长,执行非预期的次数,更会导致运行时调用栈溢出。。更不用说并发情况下的更复杂的问题。
用了单测复现根因
输出结果
Environment: