Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

overload: tcp connection refusal overload action #13311

Merged
merged 12 commits into from
Oct 13, 2020
Merged
1 change: 1 addition & 0 deletions docs/root/configuration/listeners/stats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Every listener has a statistics tree rooted at *listener.<address>.* with the fo
downstream_cx_active, Gauge, Total active connections
downstream_cx_length_ms, Histogram, Connection length milliseconds
downstream_cx_overflow, Counter, Total connections rejected due to enforcement of listener connection limit
downstream_cx_overload_reject, Counter, Total connections rejected due to configured overload actions
downstream_pre_cx_timeout, Counter, Sockets that timed out during listener filter processing
downstream_pre_cx_active, Gauge, Sockets currently undergoing listener filter processing
global_cx_overflow, Counter, Total connections rejected due to enforecement of the global connection limit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,27 @@ Overload actions

The following overload actions are supported:

.. csv-table::
:header: Name, Description
.. list-table::
:header-rows: 1
:widths: 1, 2

envoy.overload_actions.stop_accepting_requests, Envoy will immediately respond with a 503 response code to new requests
envoy.overload_actions.disable_http_keepalive, Envoy will stop accepting streams on incoming HTTP connections
envoy.overload_actions.stop_accepting_connections, Envoy will stop accepting new network connections on its configured listeners
envoy.overload_actions.shrink_heap, Envoy will periodically try to shrink the heap by releasing free memory to the system
* - Name
- Description

* - envoy.overload_actions.stop_accepting_requests
- Envoy will immediately respond with a 503 response code to new requests

* - envoy.overload_actions.disable_http_keepalive
- Envoy will stop accepting streams on incoming HTTP connections

* - envoy.overload_actions.stop_accepting_connections
- Envoy will stop accepting new network connections on its configured listeners

* - envoy.overload_actions.reject_incoming_connections
- Envoy will reject incoming connections on its configured listeners without processing any data

* - envoy.overload_actions.shrink_heap
- Envoy will periodically try to shrink the heap by releasing free memory to the system

Limiting Active Connections
---------------------------
Expand Down
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Removed Config or Runtime
New Features
------------
* 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.
* tcp: added a new :ref:`envoy.overload_actions.reject_incoming_connections <config_overload_manager_overload_actions>` action to reject incoming TCP connections.

Deprecated
----------
6 changes: 6 additions & 0 deletions include/envoy/network/connection_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ class ConnectionHandler {
*/
virtual void enableListeners() PURE;

/**
* Set the fraction of connections the listeners should reject.
* @param reject_fraction a value between 0 (reject none) and 1 (reject all).
*/
virtual void setListenerRejectFraction(float reject_fraction) PURE;

/**
* @return the stat prefix used for per-handler stats.
*/
Expand Down
12 changes: 11 additions & 1 deletion include/envoy/network/listener.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,14 @@ class TcpListenerCallbacks {
*/
virtual void onAccept(ConnectionSocketPtr&& socket) PURE;

enum class RejectCause {
GlobalCxLimit,
OverloadAction,
};
/**
* Called when a new connection is rejected.
*/
virtual void onReject() PURE;
virtual void onReject(RejectCause cause) PURE;
};

/**
Expand Down Expand Up @@ -324,6 +328,12 @@ class Listener {
* Enable accepting new connections.
*/
virtual void enable() PURE;

/**
* Set the fraction of incoming connections that will be closed immediately
* after being opened.
*/
virtual void setRejectFraction(float reject_fraction) PURE;
};

using ListenerPtr = std::unique_ptr<Listener>;
Expand Down
4 changes: 4 additions & 0 deletions include/envoy/server/overload_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ class OverloadActionNameValues {
// Overload action to stop accepting new connections.
const std::string StopAcceptingConnections = "envoy.overload_actions.stop_accepting_connections";

// Overload action to reject (accept and then close) new connections.
const std::string RejectIncomingConnections =
"envoy.overload_actions.reject_incoming_connections";

// Overload action to try to shrink the heap by releasing free memory.
const std::string ShrinkHeap = "envoy.overload_actions.shrink_heap";
};
Expand Down
4 changes: 2 additions & 2 deletions source/common/event/dispatcher_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ Network::ListenerPtr DispatcherImpl::createListener(Network::SocketSharedPtr&& s
Network::TcpListenerCallbacks& cb,
bool bind_to_port, uint32_t backlog_size) {
ASSERT(isThreadSafe());
return std::make_unique<Network::TcpListenerImpl>(*this, std::move(socket), cb, bind_to_port,
backlog_size);
return std::make_unique<Network::TcpListenerImpl>(
*this, api_.randomGenerator(), std::move(socket), cb, bind_to_port, backlog_size);
}

Network::UdpListenerPtr DispatcherImpl::createUdpListener(Network::SocketSharedPtr socket,
Expand Down
18 changes: 14 additions & 4 deletions source/common/network/tcp_listener_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ void TcpListenerImpl::onSocketEvent(short flags) {
if (rejectCxOverGlobalLimit()) {
// The global connection limit has been reached.
io_handle->close();
cb_.onReject();
cb_.onReject(TcpListenerCallbacks::RejectCause::GlobalCxLimit);
continue;
} else if (random_.bernoulli(reject_fraction_)) {
io_handle->close();
cb_.onReject(TcpListenerCallbacks::RejectCause::OverloadAction);
continue;
}

Expand Down Expand Up @@ -106,9 +110,11 @@ void TcpListenerImpl::setupServerSocket(Event::DispatcherImpl& dispatcher, Socke
}
}

TcpListenerImpl::TcpListenerImpl(Event::DispatcherImpl& dispatcher, SocketSharedPtr socket,
TcpListenerCallbacks& cb, bool bind_to_port, uint32_t backlog_size)
: BaseListenerImpl(dispatcher, std::move(socket)), cb_(cb), backlog_size_(backlog_size) {
TcpListenerImpl::TcpListenerImpl(Event::DispatcherImpl& dispatcher, Random::RandomGenerator& random,
SocketSharedPtr socket, TcpListenerCallbacks& cb,
bool bind_to_port, uint32_t backlog_size)
: BaseListenerImpl(dispatcher, std::move(socket)), cb_(cb), backlog_size_(backlog_size),
random_(random), reject_fraction_(0.0) {
if (bind_to_port) {
setupServerSocket(dispatcher, *socket_);
}
Expand All @@ -118,5 +124,9 @@ void TcpListenerImpl::enable() { file_event_->setEnabled(Event::FileReadyType::R

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

void TcpListenerImpl::setRejectFraction(const float reject_fraction) {
reject_fraction_ = reject_fraction;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you ASSERT 0 <= reject_fraction <= 1? I think the Bernoulli function correctly handles out of bounds but might as well try to check for obvious config/code bugs. (Bonus points would be to introduce a struct wrapper for the float which would check this at point of assignment, etc. but up to you.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

} // namespace Network
} // namespace Envoy
9 changes: 7 additions & 2 deletions source/common/network/tcp_listener_impl.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "envoy/common/random_generator.h"
#include "envoy/runtime/runtime.h"

#include "absl/strings/string_view.h"
Expand All @@ -13,10 +14,12 @@ namespace Network {
*/
class TcpListenerImpl : public BaseListenerImpl {
public:
TcpListenerImpl(Event::DispatcherImpl& dispatcher, SocketSharedPtr socket,
TcpListenerCallbacks& cb, bool bind_to_port, uint32_t backlog_size);
TcpListenerImpl(Event::DispatcherImpl& dispatcher, Random::RandomGenerator& random,
SocketSharedPtr socket, TcpListenerCallbacks& cb, bool bind_to_port,
uint32_t backlog_size);
void disable() override;
void enable() override;
void setRejectFraction(float reject_fraction) override;

static const absl::string_view GlobalMaxCxRuntimeKey;

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

Random::RandomGenerator& random_;
Event::FileEventPtr file_event_;
float reject_fraction_;
};

} // namespace Network
Expand Down
1 change: 1 addition & 0 deletions source/common/network/udp_listener_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class UdpListenerImpl : public BaseListenerImpl,
// Network::Listener Interface
void disable() override;
void enable() override;
void setRejectFraction(float) override {}

// Network::UdpListener Interface
Event::Dispatcher& dispatcher() override;
Expand Down
21 changes: 21 additions & 0 deletions source/server/connection_handler_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ void ConnectionHandlerImpl::addListener(absl::optional<uint64_t> overridden_list
if (disable_listeners_) {
details.listener_->pauseListening();
}
if (auto* listener = details.listener_->listener(); listener != nullptr) {
listener->setRejectFraction(listener_reject_fraction_);
}
listeners_.emplace_back(config.listenSocketFactory().localAddress(), std::move(details));
}

Expand Down Expand Up @@ -148,6 +151,13 @@ void ConnectionHandlerImpl::enableListeners() {
}
}

void ConnectionHandlerImpl::setListenerRejectFraction(float reject_fraction) {
listener_reject_fraction_ = reject_fraction;
for (auto& listener : listeners_) {
listener.second.listener_->listener()->setRejectFraction(reject_fraction);
}
}

void ConnectionHandlerImpl::ActiveTcpListener::removeConnection(ActiveTcpConnection& connection) {
ENVOY_CONN_LOG(debug, "adding to cleanup list", *connection.connection_);
ActiveConnections& active_connections = connection.active_connections_;
Expand Down Expand Up @@ -391,6 +401,17 @@ void ConnectionHandlerImpl::ActiveTcpListener::onAccept(Network::ConnectionSocke
onAcceptWorker(std::move(socket), config_->handOffRestoredDestinationConnections(), false);
}

void ConnectionHandlerImpl::ActiveTcpListener::onReject(RejectCause cause) {
switch (cause) {
case RejectCause::GlobalCxLimit:
stats_.downstream_global_cx_overflow_.inc();
break;
case RejectCause::OverloadAction:
stats_.downstream_cx_overload_reject_.inc();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please verify this stat increment in one of your tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a unit test.

break;
}
}

void ConnectionHandlerImpl::ActiveTcpListener::onAcceptWorker(
Network::ConnectionSocketPtr&& socket, bool hand_off_restored_destination_connections,
bool rebalanced) {
Expand Down
5 changes: 4 additions & 1 deletion source/server/connection_handler_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace Server {
COUNTER(downstream_cx_destroy) \
COUNTER(downstream_cx_overflow) \
COUNTER(downstream_cx_total) \
COUNTER(downstream_cx_overload_reject) \
COUNTER(downstream_global_cx_overflow) \
COUNTER(downstream_pre_cx_timeout) \
COUNTER(no_filter_chain_match) \
Expand Down Expand Up @@ -82,6 +83,7 @@ class ConnectionHandlerImpl : public Network::ConnectionHandler,
void stopListeners() override;
void disableListeners() override;
void enableListeners() override;
void setListenerRejectFraction(float reject_fraction) override;
const std::string& statPrefix() const override { return per_handler_stat_prefix_; }

/**
Expand Down Expand Up @@ -133,7 +135,7 @@ class ConnectionHandlerImpl : public Network::ConnectionHandler,

// Network::TcpListenerCallbacks
void onAccept(Network::ConnectionSocketPtr&& socket) override;
void onReject() override { stats_.downstream_global_cx_overflow_.inc(); }
void onReject(RejectCause) override;

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

class ActiveUdpListenerBase : public ConnectionHandlerImpl::ActiveListenerImplBase,
Expand Down
7 changes: 7 additions & 0 deletions source/server/worker_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ WorkerImpl::WorkerImpl(ThreadLocal::Instance& tls, ListenerHooks& hooks,
overload_manager.registerForAction(
OverloadActionNames::get().StopAcceptingConnections, *dispatcher_,
[this](OverloadActionState state) { stopAcceptingConnectionsCb(state); });
overload_manager.registerForAction(
OverloadActionNames::get().RejectIncomingConnections, *dispatcher_,
[this](OverloadActionState state) { rejectIncomingConnectionsCb(state); });
}

void WorkerImpl::addListener(absl::optional<uint64_t> overridden_listener,
Expand Down Expand Up @@ -149,5 +152,9 @@ void WorkerImpl::stopAcceptingConnectionsCb(OverloadActionState state) {
}
}

void WorkerImpl::rejectIncomingConnectionsCb(OverloadActionState state) {
handler_->setListenerRejectFraction(static_cast<float>(state.value()));
}

} // namespace Server
} // namespace Envoy
1 change: 1 addition & 0 deletions source/server/worker_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class WorkerImpl : public Worker, Logger::Loggable<Logger::Id::main> {
private:
void threadRoutine(GuardDog& guard_dog);
void stopAcceptingConnectionsCb(OverloadActionState state);
void rejectIncomingConnectionsCb(OverloadActionState state);

ThreadLocal::Instance& tls_;
ListenerHooks& hooks_;
Expand Down
2 changes: 1 addition & 1 deletion test/common/network/dns_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ class TestDnsServer : public TcpListenerCallbacks {
queries_.emplace_back(query);
}

void onReject() override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; }
void onReject(RejectCause) override { NOT_IMPLEMENTED_GCOVR_EXCL_LINE; }

void addHosts(const std::string& hostname, const IpList& ip, const RecordType& type) {
if (type == RecordType::A) {
Expand Down
Loading