1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
11 "github.com/prometheus/client_golang/prometheus"
14 // RequestCounter is an http.Handler that tracks the number of
15 // requests in progress.
16 type RequestCounter interface {
19 // Current() returns the number of requests in progress.
22 // Max() returns the maximum number of concurrent requests
23 // that will be accepted.
27 type limiterHandler struct {
28 requests chan struct{}
30 count int64 // only used if cap(requests)==0
33 // NewRequestLimiter returns a RequestCounter that delegates up to
34 // maxRequests at a time to the given handler, and responds 503 to all
35 // incoming requests beyond that limit.
37 // "concurrent_requests" and "max_concurrent_requests" metrics are
38 // registered with the given reg, if reg is not nil.
39 func NewRequestLimiter(maxRequests int, handler http.Handler, reg *prometheus.Registry) RequestCounter {
41 requests: make(chan struct{}, maxRequests),
45 reg.MustRegister(prometheus.NewGaugeFunc(
48 Name: "concurrent_requests",
49 Help: "Number of requests in progress",
51 func() float64 { return float64(h.Current()) },
53 reg.MustRegister(prometheus.NewGaugeFunc(
56 Name: "max_concurrent_requests",
57 Help: "Maximum number of concurrent requests",
59 func() float64 { return float64(h.Max()) },
65 func (h *limiterHandler) Current() int {
66 if cap(h.requests) == 0 {
67 return int(atomic.LoadInt64(&h.count))
69 return len(h.requests)
72 func (h *limiterHandler) Max() int {
73 return cap(h.requests)
76 func (h *limiterHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
77 if cap(h.requests) == 0 {
78 atomic.AddInt64(&h.count, 1)
79 defer atomic.AddInt64(&h.count, -1)
80 h.handler.ServeHTTP(resp, req)
84 case h.requests <- struct{}{}:
86 // reached max requests
87 resp.WriteHeader(http.StatusServiceUnavailable)
90 h.handler.ServeHTTP(resp, req)