#pragma once #include "envoy/access_log/access_log.h" #include "envoy/common/random_generator.h" #include "envoy/config/core/v3/health_check.pb.h" #include "envoy/data/core/v3/health_check_event.pb.h" #include "envoy/event/timer.h" #include "envoy/runtime/runtime.h" #include "envoy/stats/scope.h" #include "envoy/type/matcher/string.pb.h" #include "envoy/upstream/health_checker.h" #include "common/common/logger.h" #include "common/common/matchers.h" #include "common/network/transport_socket_options_impl.h" namespace Envoy { namespace Upstream { /** * All health checker stats. @see stats_macros.h */ #define ALL_HEALTH_CHECKER_STATS(COUNTER, GAUGE) \ COUNTER(attempt) \ COUNTER(failure) \ COUNTER(network_failure) \ COUNTER(passive_failure) \ COUNTER(success) \ COUNTER(verify_cluster) \ GAUGE(degraded, Accumulate) \ GAUGE(healthy, Accumulate) /** * Definition of all health checker stats. @see stats_macros.h */ struct HealthCheckerStats { ALL_HEALTH_CHECKER_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT) }; /** * HealthCheckerHash and HealthCheckerEqualTo are used to allow the HealthCheck proto to be used as * a flat_hash_map key. */ struct HealthCheckerHash { size_t operator()(const envoy::config::core::v3::HealthCheck& health_check) const { return MessageUtil::hash(health_check); } }; struct HealthCheckerEqualTo { bool operator()(const envoy::config::core::v3::HealthCheck& lhs, const envoy::config::core::v3::HealthCheck& rhs) const { return Protobuf::util::MessageDifferencer::Equals(lhs, rhs); } }; /** * Base implementation for all health checkers. */ class HealthCheckerImplBase : public HealthChecker, protected Logger::Loggable, public std::enable_shared_from_this { public: // Upstream::HealthChecker void addHostCheckCompleteCb(HostStatusCb callback) override { callbacks_.push_back(callback); } void start() override; std::shared_ptr transportSocketOptions() const { return transport_socket_options_; } MetadataConstSharedPtr transportSocketMatchMetadata() const { return transport_socket_match_metadata_; } protected: class ActiveHealthCheckSession : public Event::DeferredDeletable { public: ~ActiveHealthCheckSession() override; HealthTransition setUnhealthy(envoy::data::core::v3::HealthCheckFailureType type); void onDeferredDeleteBase(); void start() { onInitialInterval(); } protected: ActiveHealthCheckSession(HealthCheckerImplBase& parent, HostSharedPtr host); void handleSuccess(bool degraded = false); void handleDegraded(); void handleFailure(envoy::data::core::v3::HealthCheckFailureType type); HostSharedPtr host_; private: // Clears the pending flag if it is set. By clearing this flag we're marking the host as having // been health checked. // Returns the changed state to use following the flag update. HealthTransition clearPendingFlag(HealthTransition changed_state); virtual void onInterval() PURE; void onIntervalBase(); virtual void onTimeout() PURE; void onTimeoutBase(); virtual void onDeferredDelete() PURE; void onInitialInterval(); HealthCheckerImplBase& parent_; Event::TimerPtr interval_timer_; Event::TimerPtr timeout_timer_; uint32_t num_unhealthy_{}; uint32_t num_healthy_{}; bool first_check_{true}; }; using ActiveHealthCheckSessionPtr = std::unique_ptr; HealthCheckerImplBase(const Cluster& cluster, const envoy::config::core::v3::HealthCheck& config, Event::Dispatcher& dispatcher, Runtime::Loader& runtime, Random::RandomGenerator& random, HealthCheckEventLoggerPtr&& event_logger); ~HealthCheckerImplBase() override; virtual ActiveHealthCheckSessionPtr makeSession(HostSharedPtr host) PURE; virtual envoy::data::core::v3::HealthCheckerType healthCheckerType() const PURE; const bool always_log_health_check_failures_; const Cluster& cluster_; Event::Dispatcher& dispatcher_; const std::chrono::milliseconds timeout_; const uint32_t unhealthy_threshold_; const uint32_t healthy_threshold_; HealthCheckerStats stats_; Runtime::Loader& runtime_; Random::RandomGenerator& random_; const bool reuse_connection_; HealthCheckEventLoggerPtr event_logger_; private: struct HealthCheckHostMonitorImpl : public HealthCheckHostMonitor { HealthCheckHostMonitorImpl(const std::shared_ptr& health_checker, const HostSharedPtr& host) : health_checker_(health_checker), host_(host) {} // Upstream::HealthCheckHostMonitor void setUnhealthy() override; std::weak_ptr health_checker_; std::weak_ptr host_; }; void addHosts(const HostVector& hosts); void decHealthy(); void decDegraded(); HealthCheckerStats generateStats(Stats::Scope& scope); void incHealthy(); void incDegraded(); std::chrono::milliseconds interval(HealthState state, HealthTransition changed_state) const; std::chrono::milliseconds intervalWithJitter(uint64_t base_time_ms, std::chrono::milliseconds interval_jitter) const; void onClusterMemberUpdate(const HostVector& hosts_added, const HostVector& hosts_removed); void runCallbacks(HostSharedPtr host, HealthTransition changed_state); void setUnhealthyCrossThread(const HostSharedPtr& host); static std::shared_ptr initTransportSocketOptions(const envoy::config::core::v3::HealthCheck& config); static MetadataConstSharedPtr initTransportSocketMatchMetadata(const envoy::config::core::v3::HealthCheck& config); static const std::chrono::milliseconds NO_TRAFFIC_INTERVAL; std::list callbacks_; const std::chrono::milliseconds interval_; const std::chrono::milliseconds no_traffic_interval_; const std::chrono::milliseconds no_traffic_healthy_interval_; const std::chrono::milliseconds initial_jitter_; const std::chrono::milliseconds interval_jitter_; const uint32_t interval_jitter_percent_; const std::chrono::milliseconds unhealthy_interval_; const std::chrono::milliseconds unhealthy_edge_interval_; const std::chrono::milliseconds healthy_edge_interval_; absl::node_hash_map active_sessions_; const std::shared_ptr transport_socket_options_; const MetadataConstSharedPtr transport_socket_match_metadata_; }; class HealthCheckEventLoggerImpl : public HealthCheckEventLogger { public: HealthCheckEventLoggerImpl(AccessLog::AccessLogManager& log_manager, TimeSource& time_source, const std::string& file_name) : time_source_(time_source), file_(log_manager.createAccessLog(file_name)) {} void logEjectUnhealthy(envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host, envoy::data::core::v3::HealthCheckFailureType failure_type) override; void logAddHealthy(envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host, bool first_check) override; void logUnhealthy(envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host, envoy::data::core::v3::HealthCheckFailureType failure_type, bool first_check) override; void logDegraded(envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host) override; void logNoLongerDegraded(envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescriptionConstSharedPtr& host) override; private: void createHealthCheckEvent( envoy::data::core::v3::HealthCheckerType health_checker_type, const HostDescription& host, std::function callback) const; TimeSource& time_source_; AccessLog::AccessLogFileSharedPtr file_; }; } // namespace Upstream } // namespace Envoy