Skip to content

Commit 5fdf629

Browse files
authored
overload: tcp connection refusal overload action (envoyproxy#13311)
This will allow creating an overload action that rejects (accepts the connection and then closes it) some fraction of connections in response to overload conditions. Signed-off-by: Alex Konradi <[email protected]>
1 parent 82e7109 commit 5fdf629

File tree

18 files changed

+313
-36
lines changed

18 files changed

+313
-36
lines changed

docs/root/configuration/listeners/stats.rst

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Every listener has a statistics tree rooted at *listener.<address>.* with the fo
1717
downstream_cx_active, Gauge, Total active connections
1818
downstream_cx_length_ms, Histogram, Connection length milliseconds
1919
downstream_cx_overflow, Counter, Total connections rejected due to enforcement of listener connection limit
20+
downstream_cx_overload_reject, Counter, Total connections rejected due to configured overload actions
2021
downstream_pre_cx_timeout, Counter, Sockets that timed out during listener filter processing
2122
downstream_pre_cx_active, Gauge, Sockets currently undergoing listener filter processing
2223
global_cx_overflow, Counter, Total connections rejected due to enforecement of the global connection limit

docs/root/configuration/operations/overload_manager/overload_manager.rst

+19-6
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,27 @@ Overload actions
6868

6969
The following overload actions are supported:
7070

71-
.. csv-table::
72-
:header: Name, Description
71+
.. list-table::
72+
:header-rows: 1
7373
:widths: 1, 2
7474

75-
envoy.overload_actions.stop_accepting_requests, Envoy will immediately respond with a 503 response code to new requests
76-
envoy.overload_actions.disable_http_keepalive, Envoy will stop accepting streams on incoming HTTP connections
77-
envoy.overload_actions.stop_accepting_connections, Envoy will stop accepting new network connections on its configured listeners
78-
envoy.overload_actions.shrink_heap, Envoy will periodically try to shrink the heap by releasing free memory to the system
75+
* - Name
76+
- Description
77+
78+
* - envoy.overload_actions.stop_accepting_requests
79+
- Envoy will immediately respond with a 503 response code to new requests
80+
81+
* - envoy.overload_actions.disable_http_keepalive
82+
- Envoy will stop accepting streams on incoming HTTP connections
83+
84+
* - envoy.overload_actions.stop_accepting_connections
85+
- Envoy will stop accepting new network connections on its configured listeners
86+
87+
* - envoy.overload_actions.reject_incoming_connections
88+
- Envoy will reject incoming connections on its configured listeners without processing any data
89+
90+
* - envoy.overload_actions.shrink_heap
91+
- Envoy will periodically try to shrink the heap by releasing free memory to the system
7992

8093
Limiting Active Connections
8194
---------------------------

docs/root/version_history/current.rst

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ New Features
2727
* grpc: implemented header value syntax support when defining :ref:`initial metadata <envoy_v3_api_field_config.core.v3.GrpcService.initial_metadata>` for gRPC-based `ext_authz` :ref:`HTTP <envoy_v3_api_field_extensions.filters.http.ext_authz.v3.ExtAuthz.grpc_service>` and :ref:`network <envoy_v3_api_field_extensions.filters.network.ext_authz.v3.ExtAuthz.grpc_service>` filters, and :ref:`ratelimit <envoy_v3_api_field_config.ratelimit.v3.RateLimitServiceConfig.grpc_service>` filters.
2828
* health_check: added option to use :ref:`no_traffic_healthy_interval <envoy_v3_api_field_config.core.v3.HealthCheck.no_traffic_healthy_interval>` which allows a different no traffic interval when the host is healthy.
2929
* mongo_proxy: the list of commands to produce metrics for is now :ref:`configurable <envoy_v3_api_field_extensions.filters.network.mongo_proxy.v3.MongoProxy.commands>`.
30+
* tcp: added a new :ref:`envoy.overload_actions.reject_incoming_connections <config_overload_manager_overload_actions>` action to reject incoming TCP connections.
3031

3132
Deprecated
3233
----------

include/envoy/network/connection_handler.h

+6
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ class ConnectionHandler {
9494
*/
9595
virtual void enableListeners() PURE;
9696

97+
/**
98+
* Set the fraction of connections the listeners should reject.
99+
* @param reject_fraction a value between 0 (reject none) and 1 (reject all).
100+
*/
101+
virtual void setListenerRejectFraction(float reject_fraction) PURE;
102+
97103
/**
98104
* @return the stat prefix used for per-handler stats.
99105
*/

include/envoy/network/listener.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -197,10 +197,14 @@ class TcpListenerCallbacks {
197197
*/
198198
virtual void onAccept(ConnectionSocketPtr&& socket) PURE;
199199

200+
enum class RejectCause {
201+
GlobalCxLimit,
202+
OverloadAction,
203+
};
200204
/**
201205
* Called when a new connection is rejected.
202206
*/
203-
virtual void onReject() PURE;
207+
virtual void onReject(RejectCause cause) PURE;
204208
};
205209

206210
/**
@@ -324,6 +328,12 @@ class Listener {
324328
* Enable accepting new connections.
325329
*/
326330
virtual void enable() PURE;
331+
332+
/**
333+
* Set the fraction of incoming connections that will be closed immediately
334+
* after being opened.
335+
*/
336+
virtual void setRejectFraction(float reject_fraction) PURE;
327337
};
328338

329339
using ListenerPtr = std::unique_ptr<Listener>;

include/envoy/server/overload_manager.h

+4
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ class OverloadActionNameValues {
6262
// Overload action to stop accepting new connections.
6363
const std::string StopAcceptingConnections = "envoy.overload_actions.stop_accepting_connections";
6464

65+
// Overload action to reject (accept and then close) new connections.
66+
const std::string RejectIncomingConnections =
67+
"envoy.overload_actions.reject_incoming_connections";
68+
6569
// Overload action to try to shrink the heap by releasing free memory.
6670
const std::string ShrinkHeap = "envoy.overload_actions.shrink_heap";
6771
};

source/common/event/dispatcher_impl.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ Network::ListenerPtr DispatcherImpl::createListener(Network::SocketSharedPtr&& s
162162
Network::TcpListenerCallbacks& cb,
163163
bool bind_to_port, uint32_t backlog_size) {
164164
ASSERT(isThreadSafe());
165-
return std::make_unique<Network::TcpListenerImpl>(*this, std::move(socket), cb, bind_to_port,
166-
backlog_size);
165+
return std::make_unique<Network::TcpListenerImpl>(
166+
*this, api_.randomGenerator(), std::move(socket), cb, bind_to_port, backlog_size);
167167
}
168168

169169
Network::UdpListenerPtr DispatcherImpl::createUdpListener(Network::SocketSharedPtr socket,

source/common/network/tcp_listener_impl.cc

+15-4
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@ void TcpListenerImpl::onSocketEvent(short flags) {
6262
if (rejectCxOverGlobalLimit()) {
6363
// The global connection limit has been reached.
6464
io_handle->close();
65-
cb_.onReject();
65+
cb_.onReject(TcpListenerCallbacks::RejectCause::GlobalCxLimit);
66+
continue;
67+
} else if (random_.bernoulli(reject_fraction_)) {
68+
io_handle->close();
69+
cb_.onReject(TcpListenerCallbacks::RejectCause::OverloadAction);
6670
continue;
6771
}
6872

@@ -106,9 +110,11 @@ void TcpListenerImpl::setupServerSocket(Event::DispatcherImpl& dispatcher, Socke
106110
}
107111
}
108112

109-
TcpListenerImpl::TcpListenerImpl(Event::DispatcherImpl& dispatcher, SocketSharedPtr socket,
110-
TcpListenerCallbacks& cb, bool bind_to_port, uint32_t backlog_size)
111-
: BaseListenerImpl(dispatcher, std::move(socket)), cb_(cb), backlog_size_(backlog_size) {
113+
TcpListenerImpl::TcpListenerImpl(Event::DispatcherImpl& dispatcher, Random::RandomGenerator& random,
114+
SocketSharedPtr socket, TcpListenerCallbacks& cb,
115+
bool bind_to_port, uint32_t backlog_size)
116+
: BaseListenerImpl(dispatcher, std::move(socket)), cb_(cb), backlog_size_(backlog_size),
117+
random_(random), reject_fraction_(0.0) {
112118
if (bind_to_port) {
113119
setupServerSocket(dispatcher, *socket_);
114120
}
@@ -118,5 +124,10 @@ void TcpListenerImpl::enable() { file_event_->setEnabled(Event::FileReadyType::R
118124

119125
void TcpListenerImpl::disable() { file_event_->setEnabled(0); }
120126

127+
void TcpListenerImpl::setRejectFraction(const float reject_fraction) {
128+
ASSERT(0 <= reject_fraction && reject_fraction <= 1);
129+
reject_fraction_ = reject_fraction;
130+
}
131+
121132
} // namespace Network
122133
} // namespace Envoy

source/common/network/tcp_listener_impl.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include "envoy/common/random_generator.h"
34
#include "envoy/runtime/runtime.h"
45

56
#include "absl/strings/string_view.h"
@@ -13,10 +14,12 @@ namespace Network {
1314
*/
1415
class TcpListenerImpl : public BaseListenerImpl {
1516
public:
16-
TcpListenerImpl(Event::DispatcherImpl& dispatcher, SocketSharedPtr socket,
17-
TcpListenerCallbacks& cb, bool bind_to_port, uint32_t backlog_size);
17+
TcpListenerImpl(Event::DispatcherImpl& dispatcher, Random::RandomGenerator& random,
18+
SocketSharedPtr socket, TcpListenerCallbacks& cb, bool bind_to_port,
19+
uint32_t backlog_size);
1820
void disable() override;
1921
void enable() override;
22+
void setRejectFraction(float reject_fraction) override;
2023

2124
static const absl::string_view GlobalMaxCxRuntimeKey;
2225

@@ -33,7 +36,9 @@ class TcpListenerImpl : public BaseListenerImpl {
3336
// rejected/closed. If the accepted socket is to be admitted, false is returned.
3437
static bool rejectCxOverGlobalLimit();
3538

39+
Random::RandomGenerator& random_;
3640
Event::FileEventPtr file_event_;
41+
float reject_fraction_;
3742
};
3843

3944
} // namespace Network

source/common/network/udp_listener_impl.h

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class UdpListenerImpl : public BaseListenerImpl,
3030
// Network::Listener Interface
3131
void disable() override;
3232
void enable() override;
33+
void setRejectFraction(float) override {}
3334

3435
// Network::UdpListener Interface
3536
Event::Dispatcher& dispatcher() override;

source/server/connection_handler_impl.cc

+21
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ void ConnectionHandlerImpl::addListener(absl::optional<uint64_t> overridden_list
6666
if (disable_listeners_) {
6767
details.listener_->pauseListening();
6868
}
69+
if (auto* listener = details.listener_->listener(); listener != nullptr) {
70+
listener->setRejectFraction(listener_reject_fraction_);
71+
}
6972
listeners_.emplace_back(config.listenSocketFactory().localAddress(), std::move(details));
7073
}
7174

@@ -148,6 +151,13 @@ void ConnectionHandlerImpl::enableListeners() {
148151
}
149152
}
150153

154+
void ConnectionHandlerImpl::setListenerRejectFraction(float reject_fraction) {
155+
listener_reject_fraction_ = reject_fraction;
156+
for (auto& listener : listeners_) {
157+
listener.second.listener_->listener()->setRejectFraction(reject_fraction);
158+
}
159+
}
160+
151161
void ConnectionHandlerImpl::ActiveTcpListener::removeConnection(ActiveTcpConnection& connection) {
152162
ENVOY_CONN_LOG(debug, "adding to cleanup list", *connection.connection_);
153163
ActiveConnections& active_connections = connection.active_connections_;
@@ -391,6 +401,17 @@ void ConnectionHandlerImpl::ActiveTcpListener::onAccept(Network::ConnectionSocke
391401
onAcceptWorker(std::move(socket), config_->handOffRestoredDestinationConnections(), false);
392402
}
393403

404+
void ConnectionHandlerImpl::ActiveTcpListener::onReject(RejectCause cause) {
405+
switch (cause) {
406+
case RejectCause::GlobalCxLimit:
407+
stats_.downstream_global_cx_overflow_.inc();
408+
break;
409+
case RejectCause::OverloadAction:
410+
stats_.downstream_cx_overload_reject_.inc();
411+
break;
412+
}
413+
}
414+
394415
void ConnectionHandlerImpl::ActiveTcpListener::onAcceptWorker(
395416
Network::ConnectionSocketPtr&& socket, bool hand_off_restored_destination_connections,
396417
bool rebalanced) {

source/server/connection_handler_impl.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace Server {
3030
COUNTER(downstream_cx_destroy) \
3131
COUNTER(downstream_cx_overflow) \
3232
COUNTER(downstream_cx_total) \
33+
COUNTER(downstream_cx_overload_reject) \
3334
COUNTER(downstream_global_cx_overflow) \
3435
COUNTER(downstream_pre_cx_timeout) \
3536
COUNTER(no_filter_chain_match) \
@@ -82,6 +83,7 @@ class ConnectionHandlerImpl : public Network::ConnectionHandler,
8283
void stopListeners() override;
8384
void disableListeners() override;
8485
void enableListeners() override;
86+
void setListenerRejectFraction(float reject_fraction) override;
8587
const std::string& statPrefix() const override { return per_handler_stat_prefix_; }
8688

8789
/**
@@ -133,7 +135,7 @@ class ConnectionHandlerImpl : public Network::ConnectionHandler,
133135

134136
// Network::TcpListenerCallbacks
135137
void onAccept(Network::ConnectionSocketPtr&& socket) override;
136-
void onReject() override { stats_.downstream_global_cx_overflow_.inc(); }
138+
void onReject(RejectCause) override;
137139

138140
// ActiveListenerImplBase
139141
Network::Listener* listener() override { return listener_.get(); }
@@ -361,6 +363,7 @@ class ConnectionHandlerImpl : public Network::ConnectionHandler,
361363
std::list<std::pair<Network::Address::InstanceConstSharedPtr, ActiveListenerDetails>> listeners_;
362364
std::atomic<uint64_t> num_handler_connections_{};
363365
bool disable_listeners_;
366+
float listener_reject_fraction_{0};
364367
};
365368

366369
class ActiveUdpListenerBase : public ConnectionHandlerImpl::ActiveListenerImplBase,

source/server/worker_impl.cc

+7
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ WorkerImpl::WorkerImpl(ThreadLocal::Instance& tls, ListenerHooks& hooks,
3131
overload_manager.registerForAction(
3232
OverloadActionNames::get().StopAcceptingConnections, *dispatcher_,
3333
[this](OverloadActionState state) { stopAcceptingConnectionsCb(state); });
34+
overload_manager.registerForAction(
35+
OverloadActionNames::get().RejectIncomingConnections, *dispatcher_,
36+
[this](OverloadActionState state) { rejectIncomingConnectionsCb(state); });
3437
}
3538

3639
void WorkerImpl::addListener(absl::optional<uint64_t> overridden_listener,
@@ -149,5 +152,9 @@ void WorkerImpl::stopAcceptingConnectionsCb(OverloadActionState state) {
149152
}
150153
}
151154

155+
void WorkerImpl::rejectIncomingConnectionsCb(OverloadActionState state) {
156+
handler_->setListenerRejectFraction(static_cast<float>(state.value()));
157+
}
158+
152159
} // namespace Server
153160
} // namespace Envoy

source/server/worker_impl.h

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class WorkerImpl : public Worker, Logger::Loggable<Logger::Id::main> {
5858
private:
5959
void threadRoutine(GuardDog& guard_dog);
6060
void stopAcceptingConnectionsCb(OverloadActionState state);
61+
void rejectIncomingConnectionsCb(OverloadActionState state);
6162

6263
ThreadLocal::Instance& tls_;
6364
ListenerHooks& hooks_;

test/common/network/dns_impl_test.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ class TestDnsServer : public TcpListenerCallbacks {
281281
queries_.emplace_back(query);
282282
}
283283

284-
void onReject() override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; }
284+
void onReject(RejectCause) override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; }
285285

286286
void addHosts(const std::string& hostname, const IpList& ip, const RecordType& type) {
287287
if (type == RecordType::A) {

0 commit comments

Comments
 (0)