-
Notifications
You must be signed in to change notification settings - Fork 5.3k
server: factor out MainCommon as a class, with a run method #2568
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
Changes from all commits
e696fe1
d796786
622e5d2
6a87f69
de78e95
3e5e52e
53f3ec0
b9d284b
2ff4f4a
b28fa8e
ccd38ad
4d2bc12
4bd2da7
c84e9b4
8e0262a
928deb7
94e9b12
1763e71
2ba3207
a9237fe
57ef61f
3b469a4
f45099c
552cb2a
4914e62
58a0180
8cb20e7
ada35db
ce29a45
c2bb454
92a9544
22a2e80
d1cf0b9
6372516
1e29195
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,5 @@ | ||
| #include <iostream> | ||
| #include <memory> | ||
|
|
||
| #include "exe/main_common.h" | ||
|
|
||
| #ifdef ENVOY_HANDLE_SIGNALS | ||
| #include "exe/signal_action.h" | ||
| #endif | ||
|
|
||
| #ifdef ENVOY_HOT_RESTART | ||
| #include "server/hot_restart_impl.h" | ||
| #endif | ||
|
|
||
| #include "server/options_impl.h" | ||
|
|
||
| #include "spdlog/spdlog.h" | ||
|
|
||
| // NOLINT(namespace-envoy) | ||
|
|
||
| /** | ||
|
|
@@ -25,31 +10,28 @@ | |
| * after setting up command line options. | ||
| */ | ||
| int main(int argc, char** argv) { | ||
| #ifdef ENVOY_HANDLE_SIGNALS | ||
| // Enabled by default. Control with "bazel --define=signal_trace=disabled" | ||
| Envoy::SignalAction handle_sigs; | ||
| #endif | ||
|
|
||
| #ifdef ENVOY_HOT_RESTART | ||
| // Enabled by default, except on OS X. Control with "bazel --define=hot_restart=disabled" | ||
| const Envoy::OptionsImpl::HotRestartVersionCb hot_restart_version_cb = | ||
| [](uint64_t max_num_stats, uint64_t max_stat_name_len) { | ||
| return Envoy::Server::HotRestartImpl::hotRestartVersion(max_num_stats, max_stat_name_len); | ||
| }; | ||
| constexpr bool enable_hot_restart = true; | ||
| #else | ||
| const Envoy::OptionsImpl::HotRestartVersionCb hot_restart_version_cb = [](uint64_t, uint64_t) { | ||
| return "disabled"; | ||
| }; | ||
| constexpr bool enable_hot_restart = false; | ||
| #endif | ||
|
|
||
| std::unique_ptr<Envoy::OptionsImpl> options; | ||
| std::unique_ptr<Envoy::MainCommon> main_common; | ||
|
|
||
| // Initialize the server's main context under a try/catch loop and simply return EXIT_FAILURE | ||
| // as needed. Whatever code in the initialization path that fails is expected to log an error | ||
| // message so the user can diagnose. | ||
| try { | ||
| options = std::make_unique<Envoy::OptionsImpl>(argc, argv, hot_restart_version_cb, | ||
| spdlog::level::info); | ||
| main_common = std::make_unique<Envoy::MainCommon>(argc, argv, enable_hot_restart); | ||
| } catch (const Envoy::NoServingException& e) { | ||
| return 0; | ||
| return EXIT_SUCCESS; | ||
| } catch (const Envoy::MalformedArgvException& e) { | ||
| return 1; | ||
| return EXIT_FAILURE; | ||
| } catch (const Envoy::EnvoyException& e) { | ||
| return EXIT_FAILURE; | ||
| } | ||
| return Envoy::main_common(*options); | ||
|
|
||
| // Run the server listener loop outside try/catch blocks, so that unexpected exceptions | ||
| // show up as a core-dumps for easier diagnostis. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo: diagnostics |
||
| return main_common->run() ? EXIT_SUCCESS : EXIT_FAILURE; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,12 @@ | ||
| #include "exe/main_common.h" | ||
|
|
||
| #include <iostream> | ||
| #include <memory> | ||
|
|
||
| #include "common/common/compiler_requirements.h" | ||
| #include "common/event/libevent.h" | ||
| #include "common/network/utility.h" | ||
| #include "common/stats/stats_impl.h" | ||
| #include "common/stats/thread_local_store.h" | ||
|
|
||
| #include "server/config_validation/server.h" | ||
| #include "server/drain_manager_impl.h" | ||
|
|
@@ -22,79 +23,107 @@ | |
| #include "ares.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Server { | ||
|
|
||
| class ProdComponentFactory : public ComponentFactory { | ||
| public: | ||
| // Server::DrainManagerFactory | ||
| DrainManagerPtr createDrainManager(Instance& server) override { | ||
| return DrainManagerPtr{ | ||
| // The global drain manager only triggers on listener modification, which effectively is | ||
| // hot restart at the global level. The per-listener drain managers decide whether to | ||
| // to include /healthcheck/fail status. | ||
| new DrainManagerImpl(server, envoy::api::v2::Listener_DrainType_MODIFY_ONLY)}; | ||
| } | ||
|
|
||
| Runtime::LoaderPtr createRuntime(Server::Instance& server, | ||
| Server::Configuration::Initial& config) override { | ||
| return Server::InstanceUtil::createRuntime(server, config); | ||
| } | ||
| }; | ||
| Server::DrainManagerPtr ProdComponentFactory::createDrainManager(Server::Instance& server) { | ||
| // The global drain manager only triggers on listener modification, which effectively is | ||
| // hot restart at the global level. The per-listener drain managers decide whether to | ||
| // to include /healthcheck/fail status. | ||
| return std::make_unique<Server::DrainManagerImpl>(server, | ||
| envoy::api::v2::Listener_DrainType_MODIFY_ONLY); | ||
| } | ||
|
|
||
| } // namespace Server | ||
| Runtime::LoaderPtr ProdComponentFactory::createRuntime(Server::Instance& server, | ||
| Server::Configuration::Initial& config) { | ||
| return Server::InstanceUtil::createRuntime(server, config); | ||
| } | ||
|
|
||
| int main_common(OptionsImpl& options) { | ||
| Stats::RawStatData::configure(options); | ||
| MainCommonBase::MainCommonBase(OptionsImpl& options, bool hot_restart) : options_(options) { | ||
| ares_library_init(ARES_LIB_INIT_ALL); | ||
| Event::Libevent::Global::initialize(); | ||
| RELEASE_ASSERT(Envoy::Server::validateProtoDescriptors()); | ||
|
|
||
| switch (options_.mode()) { | ||
| case Server::Mode::Serve: { | ||
| #ifdef ENVOY_HOT_RESTART | ||
| std::unique_ptr<Server::HotRestartImpl> restarter; | ||
| try { | ||
| restarter.reset(new Server::HotRestartImpl(options)); | ||
| } catch (Envoy::EnvoyException& e) { | ||
| std::cerr << "unable to initialize hot restart: " << e.what() << std::endl; | ||
| return 1; | ||
| if (hot_restart) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah doing the --disable-hot-restart flag will be trivial now. |
||
| restarter_.reset(new Server::HotRestartImpl(options_)); | ||
| } | ||
| #endif | ||
| if (!hot_restart) { | ||
| restarter_.reset(new Server::HotRestartNopImpl()); | ||
| } | ||
|
|
||
| Stats::RawStatData::configure(options_); | ||
| tls_.reset(new ThreadLocal::InstanceImpl); | ||
| Thread::BasicLockable& log_lock = restarter_->logLock(); | ||
| Thread::BasicLockable& access_log_lock = restarter_->accessLogLock(); | ||
| auto local_address = Network::Utility::getLocalAddress(options_.localAddressIpVersion()); | ||
| Logger::Registry::initialize(options_.logLevel(), log_lock); | ||
|
|
||
| stats_store_.reset(new Stats::ThreadLocalStoreImpl(restarter_->statsAllocator())); | ||
| server_.reset(new Server::InstanceImpl(options_, local_address, default_test_hooks_, | ||
| *restarter_, *stats_store_, access_log_lock, | ||
| component_factory_, *tls_)); | ||
| break; | ||
| } | ||
| case Server::Mode::Validate: | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| Thread::BasicLockable& log_lock = restarter->logLock(); | ||
| Thread::BasicLockable& access_log_lock = restarter->accessLogLock(); | ||
| Stats::RawStatDataAllocator& stats_allocator = *restarter; | ||
| #else | ||
| std::unique_ptr<Server::HotRestartNopImpl> restarter; | ||
| restarter.reset(new Server::HotRestartNopImpl()); | ||
|
|
||
| Thread::MutexBasicLockable log_lock, access_log_lock; | ||
| Stats::HeapRawStatDataAllocator stats_allocator; | ||
| #endif | ||
| MainCommonBase::~MainCommonBase() { ares_library_cleanup(); } | ||
|
|
||
| RELEASE_ASSERT(Envoy::Server::validateProtoDescriptors()); | ||
| Event::Libevent::Global::initialize(); | ||
| Server::ProdComponentFactory component_factory; | ||
| auto local_address = Network::Utility::getLocalAddress(options.localAddressIpVersion()); | ||
| switch (options.mode()) { | ||
| bool MainCommonBase::run() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we have a better name for this? This capture historically what was in the common main, but I think logically it's about overall server setup, teardown and run. We already have a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm OK renaming it but that would also lose any kind of a/b correlation in the review. AFAIK there's no way to tell git that a file was renamed; it either figures it out or it doesn't, and mostly it doesn't :) Can this be a follow-up?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. Either later or in a final pass before merging, either works.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks; I'll put this in a follow-up. |
||
| switch (options_.mode()) { | ||
| case Server::Mode::Serve: | ||
| break; | ||
| case Server::Mode::Validate: | ||
| Thread::MutexBasicLockable log_lock; | ||
| Logger::Registry::initialize(options.logLevel(), log_lock); | ||
| return Server::validateConfig(options, local_address, component_factory) ? 0 : 1; | ||
| server_->run(); | ||
| return true; | ||
| case Server::Mode::Validate: { | ||
| auto local_address = Network::Utility::getLocalAddress(options_.localAddressIpVersion()); | ||
| return Server::validateConfig(options_, local_address, component_factory_); | ||
| } | ||
| } | ||
| NOT_REACHED; | ||
| } | ||
|
|
||
| ares_library_init(ARES_LIB_INIT_ALL); | ||
| MainCommon::MainCommon(int argc, char** argv, bool hot_restart) | ||
| : options_(computeOptions(argc, argv, hot_restart)), base_(*options_, hot_restart) {} | ||
|
|
||
| std::unique_ptr<OptionsImpl> MainCommon::computeOptions(int argc, char** argv, bool hot_restart) { | ||
| OptionsImpl::HotRestartVersionCb hot_restart_version_cb = [](uint64_t, uint64_t) { | ||
| return "disabled"; | ||
| }; | ||
|
|
||
| Logger::Registry::initialize(options.logLevel(), log_lock); | ||
| DefaultTestHooks default_test_hooks; | ||
| ThreadLocal::InstanceImpl tls; | ||
| Stats::ThreadLocalStoreImpl stats_store(stats_allocator); | ||
| #ifdef ENVOY_HOT_RESTART | ||
| if (hot_restart) { | ||
| // Enabled by default, except on OS X. Control with "bazel --define=hot_restart=disabled" | ||
| hot_restart_version_cb = [](uint64_t max_num_stats, uint64_t max_stat_name_len) { | ||
| return Server::HotRestartImpl::hotRestartVersion(max_num_stats, max_stat_name_len); | ||
| }; | ||
| } | ||
| #else | ||
| // Hot-restart should not be specified if the support is not compiled in. | ||
| RELEASE_ASSERT(!hot_restart); | ||
| #endif | ||
| return std::make_unique<OptionsImpl>(argc, argv, hot_restart_version_cb, spdlog::level::info); | ||
| } | ||
|
|
||
| // Legacy implementation of main_common. | ||
| // | ||
| // TODO(jmarantz): Remove this when all callers are removed. At that time, MainCommonBase | ||
| // and MainCommon can be merged. The current theory is that only Google calls this. | ||
| int main_common(OptionsImpl& options) { | ||
| try { | ||
| Server::InstanceImpl server(options, local_address, default_test_hooks, *restarter, stats_store, | ||
| access_log_lock, component_factory, tls); | ||
| server.run(); | ||
| } catch (const EnvoyException& e) { | ||
| ares_library_cleanup(); | ||
| return 1; | ||
| #if ENVOY_HOT_RESTART | ||
| MainCommonBase main_common(options, true); | ||
| #else | ||
| MainCommonBase main_common(options, false); | ||
| #endif | ||
| return main_common.run() ? EXIT_SUCCESS : EXIT_FAILURE; | ||
| } catch (EnvoyException& e) { | ||
| return EXIT_FAILURE; | ||
| } | ||
| ares_library_cleanup(); | ||
| return 0; | ||
| return EXIT_SUCCESS; | ||
| } | ||
|
|
||
| } // namespace Envoy | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you test with
--define signal_trace=disabled? We don't have any CI tests to catch these variants of Bazel build, so when moving things around it's probably a good idea to manually verify.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea. Doing so now. Actually, why is this a compile-time decision rather than an option? Then it would be easier to test without rebuilding the entire tree. Same with HotRestart, though that's addressed in #2568.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of these options are compile-time as we want to squash dependencies (e.g. Google gRPC) or demonstrate that site-local replacement or build time removal of features is possible (e.g. we replace during import on the Google side). Hot restart is probably a better contender to be fully dynamic, as it doesn't introduce any deps.
There's a wider conversation happening right now in #2069. Basically, we need some way to provide greater build time flexibility to allow very minimal Envoy binary deployments while also support kitchen sink builds with dynamic configuration. The former might be interesting in resource constrained embedded or container environments, the latter on a traditional fat server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't arguing against any compile-time switches, but it wasn't obvious what dependency this one had. I was thinking that if we were compiling on Windows and the APIs didn't exist, we'd just #if them out based on platform support as opposed to config, but keep the class API above that consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the
signal_tracedeps are the Backtrace library, an external dependency.