RedisWriter: publish Icinga 2 stats as a simple "keepalive"

Use case: a reader subscribes to config changes. As they happen rarely, chances
are good that the connection will time out in the meantime. Now, a reconnect is
easy - but you wouldn't know whether you missed any config changes. So you are
required to issue a new sync to be on the safe side. Given a 60sec connection
timeout (think: SSL layer, firewalls etc) when no traffic happens this would be
too expensive.

Some kind of a heartbeat available for subscription would allow subscribers to
artificially keep the connection alive. As a first simple solution to this I'd
suggest to publish CIB data, that might be useful anyways.

refs #4991
fixes #5098

Signed-off-by: Michael Friedrich <michael.friedrich@icinga.com>
This commit is contained in:
Thomas Gelf 2017-03-28 11:30:14 +02:00 committed by Michael Friedrich
parent 2f84ff84b2
commit ed2ebdf0d0
2 changed files with 31 additions and 0 deletions

View File

@ -21,6 +21,7 @@
#include "redis/rediswriter.tcpp" #include "redis/rediswriter.tcpp"
#include "remote/eventqueue.hpp" #include "remote/eventqueue.hpp"
#include "base/json.hpp" #include "base/json.hpp"
#include "base/statsfunction.hpp"
using namespace icinga; using namespace icinga;
@ -55,6 +56,11 @@ void RedisWriter::Start(bool runtimeCreated)
m_SubscriptionTimer->OnTimerExpired.connect(boost::bind(&RedisWriter::UpdateSubscriptionsTimerHandler, this)); m_SubscriptionTimer->OnTimerExpired.connect(boost::bind(&RedisWriter::UpdateSubscriptionsTimerHandler, this));
m_SubscriptionTimer->Start(); m_SubscriptionTimer->Start();
m_StatsTimer = new Timer();
m_StatsTimer->SetInterval(10);
m_StatsTimer->OnTimerExpired.connect(boost::bind(&RedisWriter::PublishStatsTimerHandler, this));
m_StatsTimer->Start();
boost::thread thread(boost::bind(&RedisWriter::HandleEvents, this)); boost::thread thread(boost::bind(&RedisWriter::HandleEvents, this));
thread.detach(); thread.detach();
} }
@ -202,6 +208,28 @@ void RedisWriter::UpdateSubscriptions(void)
<< "Current Redis event subscriptions: " << m_Subscriptions.size(); << "Current Redis event subscriptions: " << m_Subscriptions.size();
} }
void RedisWriter::PublishStatsTimerHandler(void)
{
m_WorkQueue.Enqueue(boost::bind(&RedisWriter::PublishStats, this));
}
void RedisWriter::PublishStats(void)
{
AssertOnWorkQueue();
if (!m_Context)
return;
//TODO: Figure out if more stats can be useful here.
StatsFunction::Ptr func = StatsFunctionRegistry::GetInstance()->GetItem("CIB");
Dictionary::Ptr status = new Dictionary();
Array::Ptr perfdata = new Array();
func->Invoke(status, perfdata);
String jsonStats = JsonEncode(status);
ExecuteQuery({ "PUBLISH", "icinga:stats", jsonStats });
}
void RedisWriter::HandleEvents(void) void RedisWriter::HandleEvents(void)
{ {
String queueName = Utility::NewUniqueID(); String queueName = Utility::NewUniqueID();

View File

@ -58,6 +58,8 @@ private:
void UpdateSubscriptionsTimerHandler(void); void UpdateSubscriptionsTimerHandler(void);
void UpdateSubscriptions(void); void UpdateSubscriptions(void);
void PublishStatsTimerHandler(void);
void PublishStats(void);
/* config & status dump */ /* config & status dump */
void UpdateAllConfigObjects(void); void UpdateAllConfigObjects(void);
@ -86,6 +88,7 @@ private:
boost::shared_ptr<redisReply> ExecuteQuery(const std::vector<String>& query); boost::shared_ptr<redisReply> ExecuteQuery(const std::vector<String>& query);
Timer::Ptr m_StatsTimer;
Timer::Ptr m_ReconnectTimer; Timer::Ptr m_ReconnectTimer;
Timer::Ptr m_SubscriptionTimer; Timer::Ptr m_SubscriptionTimer;
WorkQueue m_WorkQueue; WorkQueue m_WorkQueue;