Skip to content

Commit 81f46b6

Browse files
committed
add a new generic filter goaway
1 parent b6b494b commit 81f46b6

File tree

5 files changed

+421
-0
lines changed

5 files changed

+421
-0
lines changed

staging/src/k8s.io/apiserver/pkg/server/config.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,12 @@ type Config struct {
197197
// Predicate which is true for paths of long-running http requests
198198
LongRunningFunc apirequest.LongRunningRequestCheck
199199

200+
// GoawayChance is the probability that send a GOAWAY to HTTP/2 clients. When client received
201+
// GOAWAY, the in-flight requests will not be affected and new requests will use
202+
// a new TCP connection to triggering re-balancing to another server behind the load balance.
203+
// Default to 0, means never send GOAWAY. Max is 0.02 to prevent break the apiserver.
204+
GoawayChance float64
205+
200206
// MergedResourceConfig indicates which groupVersion enabled and its resources enabled/disabled.
201207
// This is composed of genericapiserver defaultAPIResourceConfig and those parsed from flags.
202208
// If not specify any in flags, then genericapiserver will only enable defaultAPIResourceConfig.
@@ -671,6 +677,9 @@ func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
671677
handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.LongRunningFunc, c.RequestTimeout)
672678
handler = genericfilters.WithWaitGroup(handler, c.LongRunningFunc, c.HandlerChainWaitGroup)
673679
handler = genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver)
680+
if c.SecureServing != nil && !c.SecureServing.DisableHTTP2 && c.GoawayChance > 0 {
681+
handler = genericfilters.WithProbabilisticGoaway(handler, c.GoawayChance)
682+
}
674683
handler = genericfilters.WithPanicRecovery(handler)
675684
return handler
676685
}

staging/src/k8s.io/apiserver/pkg/server/filters/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ go_test(
1111
srcs = [
1212
"content_type_test.go",
1313
"cors_test.go",
14+
"goaway_test.go",
1415
"maxinflight_test.go",
1516
"timeout_test.go",
1617
],
@@ -25,6 +26,7 @@ go_test(
2526
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
2627
"//staging/src/k8s.io/apiserver/pkg/endpoints/filters:go_default_library",
2728
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
29+
"//vendor/golang.org/x/net/http2:go_default_library",
2830
],
2931
)
3032

@@ -34,6 +36,7 @@ go_library(
3436
"content_type.go",
3537
"cors.go",
3638
"doc.go",
39+
"goaway.go",
3740
"longrunning.go",
3841
"maxinflight.go",
3942
"priority-and-fairness.go",
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package filters
18+
19+
import (
20+
"math/rand"
21+
"net/http"
22+
"sync"
23+
)
24+
25+
// GoawayDecider decides if server should send a GOAWAY
26+
type GoawayDecider interface {
27+
Goaway(r *http.Request) bool
28+
}
29+
30+
var (
31+
// randPool used to get a rand.Rand and generate a random number thread-safely,
32+
// which improve the performance of using rand.Rand with a locker
33+
randPool = &sync.Pool{
34+
New: func() interface{} {
35+
return rand.New(rand.NewSource(rand.Int63()))
36+
},
37+
}
38+
)
39+
40+
// WithProbabilisticGoaway returns an http.Handler that send GOAWAY probabilistically
41+
// according to the given chance for HTTP2 requests. After client receive GOAWAY,
42+
// the in-flight long-running requests will not be influenced, and the new requests
43+
// will use a new TCP connection to re-balancing to another server behind the load balance.
44+
func WithProbabilisticGoaway(inner http.Handler, chance float64) http.Handler {
45+
return &goaway{
46+
handler: inner,
47+
decider: &probabilisticGoawayDecider{
48+
chance: chance,
49+
next: func() float64 {
50+
rnd := randPool.Get().(*rand.Rand)
51+
ret := rnd.Float64()
52+
randPool.Put(rnd)
53+
return ret
54+
},
55+
},
56+
}
57+
}
58+
59+
// goaway send a GOAWAY to client according to decider for HTTP2 requests
60+
type goaway struct {
61+
handler http.Handler
62+
decider GoawayDecider
63+
}
64+
65+
// ServeHTTP implement HTTP handler
66+
func (h *goaway) ServeHTTP(w http.ResponseWriter, r *http.Request) {
67+
if r.Proto == "HTTP/2.0" && h.decider.Goaway(r) {
68+
// Send a GOAWAY and tear down the TCP connection when idle.
69+
w.Header().Set("Connection", "close")
70+
}
71+
72+
h.handler.ServeHTTP(w, r)
73+
}
74+
75+
// probabilisticGoawayDecider send GOAWAY probabilistically according to chance
76+
type probabilisticGoawayDecider struct {
77+
chance float64
78+
next func() float64
79+
}
80+
81+
// Goaway implement GoawayDecider
82+
func (p *probabilisticGoawayDecider) Goaway(r *http.Request) bool {
83+
if p.next() < p.chance {
84+
return true
85+
}
86+
87+
return false
88+
}

0 commit comments

Comments
 (0)