mirror of
https://github.com/openfaas/faas.git
synced 2025-06-19 12:36:40 +00:00
Add Concurrency Limiter
This enables limiting concurrency. It is a naive approach which will reject requests as soon as they exceed the maximum number of in-flight requests. It is a port of the following PR from the new watchdog code: https://github.com/openfaas-incubator/of-watchdog/pull/54 Signed-off-by: Sargun Dhillon <sargun@sargun.me> Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
This commit is contained in:
committed by
Alex Ellis
parent
45cf4db4cb
commit
b019f6ca54
21
watchdog/vendor/github.com/openfaas/faas-middleware/LICENSE
generated
vendored
Normal file
21
watchdog/vendor/github.com/openfaas/faas-middleware/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 OpenFaaS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
67
watchdog/vendor/github.com/openfaas/faas-middleware/concurrency-limiter/concurrency_limiter.go
generated
vendored
Normal file
67
watchdog/vendor/github.com/openfaas/faas-middleware/concurrency-limiter/concurrency_limiter.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
package limiter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type ConcurrencyLimiter struct {
|
||||
backendHTTPHandler http.Handler
|
||||
/*
|
||||
We keep two counters here in order to make it so that we can know when a request has gone to completed
|
||||
in the tests. We could wrap these up in a condvar, so there's no need to spinlock, but that seems overkill
|
||||
for testing.
|
||||
|
||||
This is effectively a very fancy semaphore built for optimistic concurrency only, and with spinlocks. If
|
||||
you want to add timeouts here / pessimistic concurrency, signaling needs to be added and/or a condvar esque
|
||||
sorta thing needs to be done to wake up waiters who are waiting post-spin.
|
||||
|
||||
Otherwise, there's all sorts of futzing in order to make sure that the concurrency limiter handler
|
||||
has completed
|
||||
The math works on overflow:
|
||||
var x, y uint64
|
||||
x = (1 << 64 - 1)
|
||||
y = (1 << 64 - 1)
|
||||
x++
|
||||
fmt.Println(x)
|
||||
fmt.Println(y)
|
||||
fmt.Println(x - y)
|
||||
Prints:
|
||||
0
|
||||
18446744073709551615
|
||||
1
|
||||
*/
|
||||
requestsStarted uint64
|
||||
requestsCompleted uint64
|
||||
|
||||
maxInflightRequests uint64
|
||||
}
|
||||
|
||||
func (cl *ConcurrencyLimiter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
requestsStarted := atomic.AddUint64(&cl.requestsStarted, 1)
|
||||
completedRequested := atomic.LoadUint64(&cl.requestsCompleted)
|
||||
if requestsStarted-completedRequested > cl.maxInflightRequests {
|
||||
// This is a failure pathway, and we do not want to block on the write to finish
|
||||
atomic.AddUint64(&cl.requestsCompleted, 1)
|
||||
w.WriteHeader(http.StatusTooManyRequests)
|
||||
fmt.Fprintf(w, "Concurrent request limit exceeded. Max concurrent requests: %d\n", cl.maxInflightRequests)
|
||||
return
|
||||
}
|
||||
cl.backendHTTPHandler.ServeHTTP(w, r)
|
||||
atomic.AddUint64(&cl.requestsCompleted, 1)
|
||||
}
|
||||
|
||||
// NewConcurrencyLimiter creates a handler which limits the active number of active, concurrent
|
||||
// requests. If the concurrency limit is less than, or equal to 0, then it will just return the handler
|
||||
// passed to it.
|
||||
func NewConcurrencyLimiter(handler http.Handler, concurrencyLimit int) http.Handler {
|
||||
if concurrencyLimit <= 0 {
|
||||
return handler
|
||||
}
|
||||
|
||||
return &ConcurrencyLimiter{
|
||||
backendHTTPHandler: handler,
|
||||
maxInflightRequests: uint64(concurrencyLimit),
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user